45 #include <rockets/jsonrpc/helpers.h>
46 #include <rockets/jsonrpc/server.h>
47 #include <rockets/server.h>
61 #ifdef BRAYNS_USE_FFMPEG
67 constexpr int64_t INTERACTIVE_THROTTLE = 1;
68 constexpr int64_t DEFAULT_THROTTLE = 50;
69 constexpr int64_t SLOW_THROTTLE = 750;
71 const int MODEL_NOT_FOUND = -12345;
72 const int INSTANCE_NOT_FOUND = -12346;
73 const int TASK_RESULT_TO_JSON_ERROR = -12347;
74 const int SCHEMA_RPC_ENDPOINT_NOT_FOUND = -12348;
75 const int PARAMETER_FROM_JSON_ERROR = -12349;
76 const int VIDEOSTREAMING_NOT_SUPPORTED = -12350;
77 const int VIDEOSTREAMING_NOT_ENABLED = -12351;
80 const std::string ENDPOINT_ANIMATION_PARAMS =
"animation-parameters";
81 const std::string ENDPOINT_APP_PARAMS =
"application-parameters";
82 const std::string ENDPOINT_CAMERA =
"camera";
83 const std::string ENDPOINT_CAMERA_PARAMS =
"camera-params";
84 const std::string ENDPOINT_RENDERER =
"renderer";
85 const std::string ENDPOINT_RENDERER_PARAMS =
"renderer-params";
86 const std::string ENDPOINT_SCENE =
"scene";
87 const std::string ENDPOINT_VOLUME_PARAMS =
"volume-parameters";
88 const std::string ENDPOINT_RENDER_PARAMS =
"rendering-parameters";
89 const std::string ENDPOINT_GEOMETRY_PARAMS =
"geometry-parameters";
90 const std::string ENDPOINT_FIELD_PARAMS =
"field-parameters";
93 const std::string ENDPOINT_STATISTICS =
"statistics";
94 const std::string ENDPOINT_VERSION =
"version";
97 const std::string METHOD_ADD_MODEL =
"add-model";
98 const std::string METHOD_SNAPSHOT =
"snapshot";
102 const std::string METHOD_ADD_CLIP_PLANE =
"add-clip-plane";
103 const std::string METHOD_GET_CLIP_PLANES =
"get-clip-planes";
104 const std::string METHOD_GET_ENVIRONMENT_MAP =
"get-environment-map";
105 const std::string METHOD_GET_INSTANCES =
"get-instances";
106 const std::string METHOD_GET_LOADERS =
"get-loaders";
107 const std::string METHOD_GET_MODEL_PROPERTIES =
"get-model-properties";
108 const std::string METHOD_GET_MODEL_TRANSFER_FUNCTION =
"get-model-transfer-function";
109 const std::string METHOD_GET_VIDEOSTREAM =
"get-videostream";
110 const std::string METHOD_IMAGE_JPEG =
"image-jpeg";
111 const std::string METHOD_SET_STREAMING_METHOD =
"image-streaming-mode";
112 const std::string METHOD_TRIGGER_JPEG_STREAM =
"trigger-jpeg-stream";
113 const std::string METHOD_INSPECT =
"inspect";
114 const std::string METHOD_MODEL_PROPERTIES_SCHEMA =
"model-properties-schema";
115 const std::string METHOD_REMOVE_CLIP_PLANES =
"remove-clip-planes";
116 const std::string METHOD_REMOVE_MODEL =
"remove-model";
117 const std::string METHOD_SCHEMA =
"schema";
118 const std::string METHOD_SET_ENVIRONMENT_MAP =
"set-environment-map";
119 const std::string METHOD_SET_MODEL_PROPERTIES =
"set-model-properties";
120 const std::string METHOD_SET_MODEL_TRANSFER_FUNCTION =
"set-model-transfer-function";
121 const std::string METHOD_SET_VIDEOSTREAM =
"set-videostream";
122 const std::string METHOD_UPDATE_CLIP_PLANE =
"update-clip-plane";
123 const std::string METHOD_UPDATE_INSTANCE =
"update-instance";
124 const std::string METHOD_UPDATE_MODEL =
"update-model";
125 const std::string METHOD_GET_LIGHTS =
"get-lights";
126 const std::string METHOD_ADD_LIGHT_SPHERE =
"add-light-sphere";
127 const std::string METHOD_ADD_LIGHT_DIRECTIONAL =
"add-light-directional";
128 const std::string METHOD_ADD_LIGHT_QUAD =
"add-light-quad";
129 const std::string METHOD_ADD_LIGHT_SPOT =
"add-light-spot";
130 const std::string METHOD_ADD_LIGHT_AMBIENT =
"add-light-ambient";
131 const std::string METHOD_REMOVE_LIGHTS =
"remove-lights";
132 const std::string METHOD_CLEAR_LIGHTS =
"clear-lights";
134 const std::string METHOD_FS_EXISTS =
"fs-exists";
135 const std::string METHOD_FS_GET_CONTENT =
"fs-get-content";
136 const std::string METHOD_FS_GET_ROOT =
"fs-get-root";
137 const std::string METHOD_FS_LIST_DIR =
"fs-list-dir";
140 const std::string METHOD_CHUNK =
"chunk";
141 const std::string METHOD_QUIT =
"quit";
142 const std::string METHOD_EXIT_LATER =
"exit-later";
143 const std::string METHOD_RESET_CAMERA =
"reset-camera";
144 const std::string METHOD_SET_CAMERA =
"set-camera";
146 const std::string LOADERS_SCHEMA =
"loaders-schema";
148 const std::string JSON_TYPE =
"application/json";
150 using Response = rockets::jsonrpc::Response;
152 std::string hyphenatedToCamelCase(
const std::string& hyphenated)
154 std::string camel = hyphenated;
156 for (
size_t x = 0; x < camel.length(); x++)
160 std::string tempString = camel.substr(x + 1, 1);
162 transform(tempString.begin(), tempString.end(), tempString.begin(), toupper);
165 camel.insert(x, tempString);
168 camel[0] = toupper(camel[0]);
172 std::string getNotificationEndpointName(
const std::string& endpoint)
174 return "set-" + endpoint;
177 std::string getRequestEndpointName(
const std::string& endpoint)
179 return "get-" + endpoint;
182 const Response::Error VIDEOSTREAM_NOT_ENABLED_ERROR{
"Core was not started with videostream support enabled",
183 VIDEOSTREAMING_NOT_ENABLED};
184 const Response::Error VIDEOSTREAM_NOT_SUPPORTED_ERROR{
"Core was not build with videostream support",
185 VIDEOSTREAMING_NOT_SUPPORTED};
190 template <
class T,
class PRE>
191 bool preUpdate(
const std::string& json, PRE preUpdateFunc,
192 typename std::enable_if<!std::is_abstract<T>::value>::type* = 0)
194 if (std::function<
bool(
const T&)>(preUpdateFunc))
197 if (!staticjson::from_json_string(json.c_str(), &temp,
nullptr))
199 if (!preUpdateFunc(temp))
205 template <
class T,
class PRE>
206 bool preUpdate(
const std::string&, PRE,
typename std::enable_if<std::is_abstract<T>::value>::type* = 0)
211 template <
class T,
class PRE,
class POST>
213 T& obj,
const std::string& json, PRE preUpdateFunc = [] {}, POST postUpdateFunc = [] {})
215 staticjson::ParseStatus status;
217 if (!preUpdate<T>(json, preUpdateFunc))
220 const auto success = staticjson::from_json_string(json.c_str(), &obj, &status);
224 if (std::function<
void(T&)>(postUpdateFunc))
240 #ifdef USE_NETWORKING
241 if (uvw::Loop::getDefault()->alive())
243 _processDelayedNotifies = uvw::Loop::getDefault()->resource<uvw::AsyncHandle>();
244 _processDelayedNotifies->on<uvw::AsyncEvent>([&](
const auto&,
auto&) { this->
processDelayedNotifies(); });
252 decltype(
_tasks) tasksToCancel;
258 for (
const auto& entry : tasksToCancel)
260 entry.first->cancel();
261 entry.second->wait();
264 #ifdef USE_NETWORKING
265 if (_processDelayedNotifies)
266 _processDelayedNotifies->close();
273 i->clearModifiedCallback();
288 catch (
const std::exception& exc)
290 CORE_ERROR(
"Error while handling HTTP/websocket messages: " << exc.what());
306 #ifdef BRAYNS_USE_FFMPEG
316 [action,
this](
const auto& request)
318 ScopedCurrentClient scope(this->_currentClientID, request.clientID);
319 action(jsonToPropertyMap(request.message));
328 [action,
this](
const auto& request)
330 ScopedCurrentClient scope(this->_currentClientID, request.clientID);
342 [name = desc.
methodName, action](
const auto& request)
346 return Response{to_json(action(jsonToPropertyMap(request.message)))};
350 return Response{Response::Error{
"from_json for " + name +
" failed", PARAMETER_FROM_JSON_ERROR}};
361 [action,
this](
const auto& request)
363 ScopedCurrentClient scope(this->_currentClientID, request.clientID);
364 return Response{to_json(action())};
372 _jsonrpcServer->bind(name,
373 [action,
this](
const auto& request)
376 return Response{action(request.message)};
382 _jsonrpcServer->bind(name,
383 [action,
this](
const auto& request)
386 return Response{action()};
392 _jsonrpcServer->connect(name,
393 [action,
this](
const auto& request)
396 action(request.message);
402 _jsonrpcServer->connect(name,
403 [action,
this](
const auto& request)
414 std::vector<std::function<void()>> delayedNotifies;
416 std::lock_guard<std::mutex> lock(_delayedNotifiesMutex);
417 delayedNotifies = std::move(_delayedNotifies);
420 for (
const auto& func : delayedNotifies)
428 const auto& appParams = _parametersManager.getApplicationParameters();
429 #ifdef USE_NETWORKING
430 if (uvw::Loop::getDefault()->alive())
433 std::make_unique<rockets::Server>(uv_default_loop(), appParams.getHttpServerURI(),
"rockets");
434 _manualProcessing =
false;
438 _rocketsServer = std::make_unique<rockets::Server>(appParams.getHttpServerURI(),
"rockets", 0);
440 CORE_INFO(
"Rockets server running on " << _rocketsServer->getURI());
442 _jsonrpcServer = std::make_unique<JsonRpcServer>(*_rocketsServer);
444 _parametersManager.getApplicationParameters().setHttpServerURI(_rocketsServer->getURI());
446 catch (
const std::runtime_error& e)
448 CORE_ERROR(
"Rockets server could not be initialized: '" << e.what() <<
"'");
458 _rocketsServer->handleClose(
459 [
this](
const uintptr_t clientID)
461 _binaryRequests.removeRequest(clientID);
462 CORE_DEBUG(
"Closing WebSocket with client " << clientID);
463 return std::vector<rockets::ws::Response>{};
466 _rocketsServer->handleBinary(
467 std::bind(&BinaryRequests::processMessage, std::ref(_binaryRequests), std::placeholders::_1));
473 std::lock_guard<std::mutex> lock(_delayedNotifiesMutex);
474 _delayedNotifies.push_back(notify);
477 #ifdef USE_NETWORKING
478 if (_processDelayedNotifies)
482 _processDelayedNotifies->send();
487 void _rebroadcast(
const std::string& endpoint,
const std::string& message,
const std::set<uintptr_t>& filter)
492 if (_rocketsServer->getConnectionCount() > 1)
496 const auto& msg = rockets::jsonrpc::makeNotification(endpoint, message);
497 _rocketsServer->broadcastText(msg, filter);
499 catch (
const std::exception& e)
501 CORE_ERROR(
"Error rebroadcasting notification: " << e.what());
513 : _currentClientID(currentClientID)
515 _currentClientID = newID;
516 CORE_DEBUG(
"New WebSocket connection established with client " << _currentClientID);
522 uintptr_t& _currentClientID;
525 void _bindEndpoint(
const std::string& method, rockets::jsonrpc::ResponseCallback action)
527 _jsonrpcServer->bind(method,
528 [&, action](rockets::jsonrpc::Request request)
531 return action(request);
535 template <
typename Params,
typename RetVal>
536 void _bindEndpoint(
const std::string& method, std::function<RetVal(Params)> action)
538 _jsonrpcServer->bind(method,
539 [&, action](rockets::jsonrpc::Request request)
544 if (!::
from_json(params, request.message))
545 return Response::invalidParams();
548 const auto& ret = action(std::move(params));
551 catch (
const rockets::jsonrpc::response_error& e)
553 return Response{Response::Error{e.what(), e.code}};
558 template <
typename RetVal>
559 void _bindEndpoint(
const std::string& method, std::function<RetVal()> action)
561 _jsonrpcServer->bind(method,
562 [&, action](rockets::jsonrpc::Request request)
568 const auto& ret = action();
571 catch (
const rockets::jsonrpc::response_error& e)
573 return Response{Response::Error{e.what(), e.code}};
578 template <
typename Params>
579 void _bindEndpoint(
const std::string& method, std::function<
void(Params)> action)
581 _jsonrpcServer->bind(method,
582 [&, action](rockets::jsonrpc::Request request)
587 if (!::
from_json(params, request.message))
588 return Response::invalidParams();
589 action(std::move(params));
590 return Response{
"\"OK\""};
594 void _bindEndpoint(
const std::string& method, rockets::jsonrpc::VoidCallback action)
596 _jsonrpcServer->connect(method,
597 [&, action](rockets::jsonrpc::Request request)
601 return Response{
"\"OK\""};
608 _jsonrpcServer->bind(method,
609 [&, key, action](rockets::jsonrpc::Request request)
613 using namespace rapidjson;
615 document.Parse(request.message.c_str());
617 if (!document.HasMember(
"id") || !document.HasMember(key.c_str()))
618 return Response::invalidParams();
620 const auto modelID = document[
"id"].GetInt();
621 auto model = _engine.getScene().getModel(modelID);
624 return Response{Response::Error{
"Model not found", MODEL_NOT_FOUND}};
629 Document propertyDoc;
630 propertyDoc.SetObject() = document[key.c_str()].GetObject();
631 rapidjson::StringBuffer buffer;
632 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
633 propertyDoc.Accept(writer);
635 if (!action(buffer.GetString(), model))
636 return Response::invalidParams();
638 _engine.triggerRender();
639 _rebroadcast(method, request.message, {request.clientID});
640 return Response{
to_json(
true)};
645 void _handleGET(
const std::string& endpoint, T& obj,
const int64_t throttleTime = DEFAULT_THROTTLE)
647 using namespace rockets::http;
649 _rocketsServer->handle(Method::GET, endpoint,
650 [&obj](
const Request&)
651 {
return make_ready_response(Code::OK,
to_json(obj), JSON_TYPE); });
653 _handleObjectSchema(endpoint, obj);
655 const std::string rpcEndpoint = getRequestEndpointName(endpoint);
657 _jsonrpcServer->bind(rpcEndpoint, std::function<
const T&()>([&obj]() ->
const T& {
return obj; }));
658 _handleSchema(rpcEndpoint,
665 [&, endpoint = getNotificationEndpointName(endpoint), throttleTime](
const auto& base)
667 auto& throttle = _throttle[endpoint];
671 std::lock_guard<std::mutex> lock(throttle.first);
673 const auto& castedObj =
static_cast<const T&
>(base);
675 [&rocketsServer = _rocketsServer, clientID = _currentClientID, endpoint, json =
to_json(castedObj)]
677 if (rocketsServer->getConnectionCount() == 0)
681 const auto& msg = rockets::jsonrpc::makeNotification(endpoint, json);
682 CORE_DEBUG(
"Broadcasting to " << clientID <<
" [" << endpoint <<
"]");
684 if (clientID == NO_CURRENT_CLIENT)
685 rocketsServer->broadcastText(msg);
687 rocketsServer->broadcastText(msg, {clientID});
690 if (endpoint != METHOD_SET_CAMERA)
693 rocketsServer->broadcastText(msg);
694 rocketsServer->broadcastText(msg, {clientID});
697 catch (
const std::exception& e)
699 CORE_ERROR(
"Error broadcasting notification: " << e.what());
702 const auto delayedNotify = [&, notify] { this->_delayedNotify(notify); };
706 if (_currentClientID == NO_CURRENT_CLIENT)
707 throttle.second(notify, delayedNotify, throttleTime);
709 throttle.second(delayedNotify, delayedNotify, throttleTime);
712 _objects.push_back(&obj);
718 _handlePUT(endpoint, obj, std::function<
bool(
const T&)>(), std::function<
void(T&)>());
721 template <
class T,
class PRE,
class POST>
722 void _handlePUT(
const std::string& endpoint, T& obj, PRE preUpdateFunc, POST postUpdateFunc)
724 using namespace rockets::http;
725 _rocketsServer->handle(Method::PUT, endpoint,
726 [&obj, preUpdateFunc, postUpdateFunc](
const Request& req)
728 return make_ready_response(
from_json(obj, req.body, preUpdateFunc, postUpdateFunc)
730 : Code::BAD_REQUEST);
733 _handleObjectSchema(endpoint, obj);
735 const std::string rpcEndpoint = getNotificationEndpointName(endpoint);
737 _bindEndpoint(rpcEndpoint,
738 [&, rpcEndpoint, preUpdateFunc, postUpdateFunc](rockets::jsonrpc::Request request)
740 if (
from_json(obj, request.message, preUpdateFunc, postUpdateFunc))
742 _engine.triggerRender();
743 return rockets::jsonrpc::Response{to_json(true)};
745 return rockets::jsonrpc::Response::invalidParams();
748 _handleSchema(rpcEndpoint, buildJsonRpcSchemaRequest<T, bool>(desc, obj));
752 void _handle(
const std::string& endpoint, T& obj,
const int64_t throttleTime = DEFAULT_THROTTLE)
754 _handleGET(endpoint, obj, throttleTime);
755 _handlePUT(endpoint, obj);
758 template <
class P,
class R>
762 _handleSchema(desc.
methodName, buildJsonRpcSchemaRequest<P, R>(desc));
769 _handleSchema(desc.
methodName, buildJsonRpcSchemaRequestReturnOnly<R>(desc));
776 _handleSchema(desc.
methodName, buildJsonRpcSchemaNotify<P>(desc));
785 template <
class P,
class R>
787 std::function<rockets::jsonrpc::CancelRequestCallback(
788 P, uintptr_t, rockets::jsonrpc::AsyncResponse, rockets::jsonrpc::ProgressUpdateCallback)>
791 _jsonrpcServer->bindAsync<P>(desc.
methodName, action);
792 _handleSchema(desc.
methodName, buildJsonRpcSchemaRequest<P, R>(desc));
795 template <
class P,
class R>
797 std::function<std::shared_ptr<
Task<R>>(P, uintptr_t)> createTask)
806 auto action = [&, createTask](P params,
auto clientID,
auto respond,
auto progressCb)
811 const Response response(Response::Error{error.what(), error.code, error.data});
812 this->_delayedNotify([respond, response] { respond(response); });
818 auto readyCallback = [&, respond](
const R& result)
822 this->_delayedNotify([respond, result] { respond({
to_json(result)}); });
824 catch (
const std::runtime_error& e)
826 const Response response(Response::Error{e.what(), TASK_RESULT_TO_JSON_ERROR});
827 this->_delayedNotify([respond, response] { respond(response); });
832 auto task = createTask(std::move(params), clientID);
834 std::function<void()> finishProgress = [task] { task->progress.update(
"Done", 1.f); };
836 #ifdef USE_NETWORKING
838 if (uvw::Loop::getDefault()->alive())
840 auto progressUpdate = uvw::Loop::getDefault()->resource<uvw::TimerHandle>();
842 auto sendProgress = [progressCb, &progress = task->progress] { progress.consume(progressCb); };
843 progressUpdate->on<uvw::TimerEvent>([sendProgress](
const auto&,
auto&) { sendProgress(); });
845 finishProgress = [task, progressUpdate, sendProgress]
847 task->progress.update(
"Done", 1.f);
849 progressUpdate->stop();
850 progressUpdate->close();
853 using ms = std::chrono::milliseconds;
854 progressUpdate->start(ms(0), ms(SLOW_THROTTLE));
860 auto responseTask = std::make_shared<async::task<void>>(task->get().then(
861 [&, readyCallback, errorCallback, task, finishProgress](
typename Task<R>::Type result)
867 readyCallback(result.
get());
873 catch (
const std::exception& e)
875 errorCallback({e.what()});
877 catch (
const async::task_canceled&)
879 task->finishCancel();
882 #ifdef USE_NETWORKING
883 if (_processDelayedNotifies)
884 _processDelayedNotifies->send();
887 std::lock_guard<std::mutex> lock(_tasksMutex);
889 _binaryRequests.removeTask(task);
892 std::lock_guard<std::mutex> lock(_tasksMutex);
893 _tasks.emplace(task, responseTask);
896 auto cancel = [task, responseTask](
auto done) { task->cancel(done); };
900 return rockets::jsonrpc::CancelRequestCallback(cancel);
907 catch (
const std::exception& e)
909 errorCallback({e.what()});
911 return rockets::jsonrpc::CancelRequestCallback();
913 _handleAsyncRPC<P, R>(desc, action);
919 _handleSchema(endpoint, buildJsonSchema<T>(hyphenatedToCamelCase(endpoint)));
925 _handleSchema(endpoint,
buildJsonSchema(obj, hyphenatedToCamelCase(endpoint)));
930 using namespace rockets::http;
931 _rocketsServer->handle(Method::GET, endpoint +
"/schema",
932 [schema](
const Request&) {
return make_ready_response(Code::OK, schema, JSON_TYPE); });
934 _schemas[endpoint] = schema;
939 _handleAnimationParams();
942 _handleTriggerImageStream();
943 _handleSetImageStreamingMode();
947 _handle(ENDPOINT_APP_PARAMS, _parametersManager.getApplicationParameters());
948 _handle(ENDPOINT_VOLUME_PARAMS, _parametersManager.getVolumeParameters());
949 _handle(ENDPOINT_FIELD_PARAMS, _parametersManager.getFieldParameters());
950 _handle(ENDPOINT_RENDER_PARAMS, _parametersManager.getRenderingParameters());
951 _handle(ENDPOINT_GEOMETRY_PARAMS, _parametersManager.getGeometryParameters());
954 const bool disableBroadcasting = std::getenv(
"ROCKETS_DISABLE_SCENE_BROADCASTING") !=
nullptr;
955 if (disableBroadcasting)
957 CORE_WARN(
"Scene broadcasting has been disabled");
960 _handle(ENDPOINT_SCENE, _engine.getScene());
962 _handleGET(ENDPOINT_STATISTICS, _engine.getStatistics(), SLOW_THROTTLE);
969 _handleResetCamera();
972 _handleRequestModelUpload();
975 _handleSetEnvironmentMap();
976 _handleGetEnvironmentMap();
978 _handleSetVideostream();
979 _handleGetVideostream();
982 _handleRemoveModel();
983 _handleUpdateModel();
984 _handleSetModelProperties();
985 _handleGetModelProperties();
986 _handleModelPropertiesSchema();
988 _handleSetModelTransferFunction();
989 _handleGetModelTransferFunction();
991 _handleAddClipPlane();
992 _handleGetClipPlanes();
993 _handleUpdateClipPlane();
994 _handleRemoveClipPlanes();
996 _handleGetInstances();
997 _handleUpdateInstance();
1000 _handleLoadersSchema();
1001 _handlePropertyObject(_engine.getCamera(), ENDPOINT_CAMERA_PARAMS,
"camera");
1002 _handlePropertyObject(_engine.getRenderer(), ENDPOINT_RENDERER_PARAMS,
"renderer");
1005 _handleRemoveLights();
1006 _handleClearLights();
1009 _handleFsGetContent();
1011 _handleFsGetListDir();
1013 _endpointsRegistered =
true;
1018 _jsonrpcServer->bind(METHOD_IMAGE_JPEG,
1022 return _imageGenerator.createImage(
1023 _engine.getFrameBuffer(),
"jpg",
1024 _parametersManager.getApplicationParameters().getJpegCompression());
1026 _handleSchema(METHOD_IMAGE_JPEG, buildJsonRpcSchemaRequestReturnOnly<ImageGenerator::ImageBase64>(
1027 {METHOD_IMAGE_JPEG,
"Get the current state of " + METHOD_IMAGE_JPEG}));
1032 auto& frameBuffer = _engine.getFrameBuffer();
1033 if (frameBuffer.getFrameBufferFormat() == FrameBufferFormat::none || !frameBuffer.isModified())
1038 const auto& params = _parametersManager.getApplicationParameters();
1039 const auto fps = params.getImageStreamFPS();
1043 const auto elapsed = _timer.elapsed() + _leftover;
1044 const auto duration = 1.0 / fps;
1045 if (elapsed < duration)
1048 _leftover = elapsed - duration;
1049 for (; _leftover > duration;)
1050 _leftover -= duration;
1053 const auto image = _imageGenerator.createJPEG(frameBuffer, params.getJpegCompression());
1055 _rocketsServer->broadcastBinary((
const char*)image.data.get(), image.size);
1060 if (!_controlledStreamingFlag.load())
1065 auto& frameBuffer = _engine.getFrameBuffer();
1066 if (frameBuffer.getFrameBufferFormat() == FrameBufferFormat::none || !frameBuffer.isModified())
1071 _controlledStreamingFlag =
false;
1072 const auto& params = _parametersManager.getApplicationParameters();
1074 const auto image = _imageGenerator.createJPEG(frameBuffer, params.getJpegCompression());
1076 _rocketsServer->broadcastBinary((
const char*)image.data.get(), image.size);
1079 #ifdef BRAYNS_USE_FFMPEG
1080 void _broadcastVideo()
1082 if (!_videoParams.enabled)
1085 if (_videoUpdatedResponse)
1086 _videoUpdatedResponse();
1087 _videoUpdatedResponse =
nullptr;
1091 const auto& params = _parametersManager.getApplicationParameters();
1092 const auto fps = params.getImageStreamFPS();
1096 if (_encoder && _encoder->kbps != _videoParams.kbps)
1099 auto& frameBuffer = _engine.getFrameBuffer();
1102 int width = frameBuffer.getFrameSize().x;
1105 int height = frameBuffer.getFrameSize().y;
1106 if (height % 2 != 0)
1109 _encoder = std::make_unique<Encoder>(width, height, fps, _videoParams.kbps,
1110 [&rs = _rocketsServer](
auto a,
auto b) { rs->broadcastBinary(a, b); });
1113 if (_videoUpdatedResponse)
1114 _videoUpdatedResponse();
1115 _videoUpdatedResponse =
nullptr;
1117 if (frameBuffer.getFrameBufferFormat() == FrameBufferFormat::none || !frameBuffer.isModified())
1122 _encoder->encode(frameBuffer);
1128 static core::Version version;
1129 using namespace rockets::http;
1130 _rocketsServer->handleGET(ENDPOINT_VERSION, version);
1131 _rocketsServer->handle(Method::GET, ENDPOINT_VERSION +
"/schema",
1133 {
return make_ready_response(Code::OK, version.getSchema(), JSON_TYPE); });
1135 _jsonrpcServer->bind(
1136 getRequestEndpointName(ENDPOINT_VERSION), (std::function<core::Version()>)[] {
return core::Version(); });
1138 _handleSchema(ENDPOINT_VERSION, version.getSchema());
1143 auto& animParams = _parametersManager.getAnimationParameters();
1146 _handleGET(ENDPOINT_ANIMATION_PARAMS, animParams, INTERACTIVE_THROTTLE);
1152 auto& camera = _engine.getCamera();
1155 if (obj.getCurrentType().empty())
1157 return std::find(types.begin(), types.end(), obj.getCurrentType()) != types.end();
1159 _handleGET(ENDPOINT_CAMERA, camera);
1160 _handlePUT(ENDPOINT_CAMERA, camera,
preUpdate, std::function<
void(
Camera&)>());
1165 auto& renderer = _engine.getRenderer();
1168 if (obj.getCurrentType().empty())
1170 return std::find(types.begin(), types.end(), obj.getCurrentType()) != types.end();
1172 _handleGET(ENDPOINT_RENDERER, renderer);
1173 _handlePUT(ENDPOINT_RENDERER, renderer,
preUpdate, std::function<
void(
Renderer&)>());
1179 "endpoint",
"name of the endpoint to get its schema"};
1181 _jsonrpcServer->bind(
1183 [&schemas = _schemas](
const auto& request)
1186 if (::
from_json(param, request.message))
1188 if (schemas.count(param.endpoint) == 0)
1189 return Response{Response::Error{
"Endpoint not found", SCHEMA_RPC_ENDPOINT_NOT_FOUND}};
1191 auto schema = schemas[param.
endpoint];
1192 return Response{std::move(schema)};
1194 return Response::invalidParams();
1197 _handleSchema(METHOD_SCHEMA, buildJsonRpcSchemaRequest<SchemaParam, std::string>(desc));
1202 using Position = std::array<double, 2>;
1204 "position",
"x-y position in normalized coordinates"};
1205 _handleRPC<Position, Renderer::PickResult>(
1207 [&engine = _engine](
const auto& position) {
1208 return engine.getRenderer().pick({float(position[0]), float(position[1])});
1214 _handleRPC({METHOD_QUIT,
"Quit the application"},
1217 engine.setKeepRunning(
false);
1218 engine.triggerRender();
1224 _handleRPC<ExitLaterSchedule>({METHOD_EXIT_LATER,
"Schedules Core to shutdown after a given amount of minutes",
1225 "minutes",
"Number of minutes after which Core will shut down"},
1228 std::lock_guard<std::mutex> lock(_scheduleMutex);
1229 if (schedule.minutes > 0)
1231 if (_scheduledShutdownActive)
1233 _cancelScheduledShutdown =
true;
1234 _monitor.notify_all();
1235 _shutDownWorker->join();
1236 _shutDownWorker.reset();
1237 _cancelScheduledShutdown =
false;
1238 _scheduledShutdownActive =
false;
1241 _scheduledShutdownActive =
true;
1242 const uint32_t mins = schedule.minutes;
1243 _shutDownWorker = std::unique_ptr<std::thread>(
new std::thread(
1246 std::chrono::milliseconds timeToWait(mins * 60000);
1247 std::unique_lock<std::mutex> threadLock(_waitLock);
1248 _monitor.wait_for(threadLock, timeToWait);
1249 if (!_cancelScheduledShutdown)
1251 _engine.setKeepRunning(
false);
1252 _engine.triggerRender();
1261 _handleRPC({METHOD_RESET_CAMERA,
"Resets the camera to its initial values"},
1264 engine.getCamera().reset();
1265 engine.triggerRender();
1272 "settings",
"Snapshot settings for quality and size"};
1273 auto func = [&engine = _engine, &imageGenerator = _imageGenerator](
auto&& params,
const auto)
1276 return std::make_shared<SnapshotTask>(
SnapshotFunctor{engine, std::move(params), imageGenerator});
1278 _handleTask<SnapshotParams, ImageGenerator::ImageBase64>(desc, func);
1283 _handleRPC({METHOD_TRIGGER_JPEG_STREAM,
"Triggers the engine to stream a frame to the clients"},
1284 [&] { _triggerControlledStreaming(); });
1289 _handleRPC<ImageStreamingMethod>({METHOD_SET_STREAMING_METHOD,
1290 "Set the image streaming method between automatic or "
1292 "type",
"Streaming type, either \"stream\" or \"quanta\""},
1295 if (method.type ==
"quanta")
1297 _useControlledStream =
true;
1298 _controlledStreamingFlag =
false;
1301 _useControlledStream =
false;
1308 "Request upload of blob to trigger adding of model after blob has "
1309 "been received; returns model descriptor on success",
1310 Execution::async,
"param",
"size, type, name, transformation, etc."};
1312 _handleTask<BinaryParam, ModelDescriptorPtr>(desc, std::bind(&BinaryRequests::createTask,
1313 std::ref(_binaryRequests), std::placeholders::_1,
1314 std::placeholders::_2, std::ref(_engine)));
1320 "chunk",
"object with an ID of the chunk"};
1322 _handleRPC<Chunk>(desc, [&req = _binaryRequests](
const auto& chunk) { req.setNextChunkID(chunk.id); });
1328 "Set the transfer function of the given model",
"param",
1329 "model ID and the new transfer function"};
1331 _bindModelEndpoint(METHOD_SET_MODEL_TRANSFER_FUNCTION,
"transfer_function",
1334 auto& tf = model->getModel().getTransferFunction();
1342 _handleSchema(METHOD_SET_MODEL_TRANSFER_FUNCTION, buildJsonRpcSchemaRequest<ModelTransferFunction, bool>(desc));
1348 "Get the transfer function of the given model",
"id",
"the model ID"};
1351 [&engine = _engine](
const ObjectID& id)
1353 auto model = engine.getScene().getModel(
id.
id);
1355 throw rockets::jsonrpc::response_error(
1356 "Model not found", MODEL_NOT_FOUND);
1357 return model->getModel().getTransferFunction();
1360 _handleSchema(METHOD_GET_MODEL_TRANSFER_FUNCTION, buildJsonRpcSchemaRequest<ObjectID, TransferFunction>(desc));
1365 const RpcParameterDescription desc{METHOD_ADD_CLIP_PLANE,
"Add a clip plane; returns the clip plane descriptor",
1366 "plane",
"An array of 4 floats"};
1368 _handleRPC<Plane, ClipPlanePtr>(desc,
1371 auto& scene = _engine.getScene();
1372 auto clipPlane = scene.getClipPlane(scene.addClipPlane(
plane));
1373 _rebroadcast(METHOD_UPDATE_CLIP_PLANE,
to_json(clipPlane),
1374 {_currentClientID});
1381 _handleRPC<ClipPlanes>({METHOD_GET_CLIP_PLANES,
"Get all clip planes"},
1382 [&engine = _engine]()
1384 auto& scene = engine.getScene();
1385 return scene.getClipPlanes();
1391 const RpcParameterDescription desc{METHOD_UPDATE_CLIP_PLANE,
"Update a clip plane with the given coefficients",
1392 "clip_plane",
"Plane id and equation"};
1393 _handleRPC<ClipPlane, bool>(desc,
1396 auto& scene = _engine.getScene();
1397 if (
auto plane = scene.getClipPlane(newPlane.
getID()))
1399 plane->setPlane(newPlane.getPlane());
1400 _rebroadcast(METHOD_UPDATE_CLIP_PLANE, to_json(newPlane),
1401 {_currentClientID});
1402 _engine.triggerRender();
1412 "Remove clip planes from the scene given their gids",
"ids",
1413 "Array of clip planes IDs"};
1414 _handleRPC<size_ts, bool>(desc,
1417 for (
const auto id : ids)
1418 _engine.getScene().removeClipPlane(
id);
1419 _rebroadcast(METHOD_REMOVE_CLIP_PLANES,
to_json(ids), {_currentClientID});
1420 _engine.triggerRender();
1428 _bindEndpoint(METHOD_GET_LIGHTS,
1429 [&engine = _engine](
const rockets::jsonrpc::Request& )
1431 const auto& lights = engine.getScene().getLightManager().getLights();
1433 std::vector<std::string> jsonStrings;
1435 for (
const auto& kv : lights)
1438 rpcLight.
id = kv.first;
1439 auto baseLight = kv.second;
1441 switch (baseLight->_type)
1443 case LightType::DIRECTIONAL:
1445 rpcLight.type =
"directional";
1446 const auto light = static_cast<DirectionalLight*>(baseLight.get());
1447 rpcLight.properties.setProperty({
"direction", toArray<3, double>(light->_direction)});
1448 rpcLight.properties.setProperty({
"angularDiameter", light->_angularDiameter});
1451 case LightType::SPHERE:
1453 rpcLight.type =
"sphere";
1454 const auto light = static_cast<SphereLight*>(baseLight.get());
1455 rpcLight.properties.setProperty({
"position", toArray<3, double>(light->_position)});
1459 case LightType::QUAD:
1461 rpcLight.type =
"quad";
1462 const auto light =
static_cast<QuadLight*
>(baseLight.get());
1463 rpcLight.properties.setProperty({
"position", toArray<3, double>(light->_position)});
1464 rpcLight.properties.setProperty({
"edge1", toArray<3, double>(light->_edge1)});
1465 rpcLight.properties.setProperty({
"edge2", toArray<3, double>(light->_edge2)});
1468 case LightType::SPOTLIGHT:
1470 rpcLight.type =
"spotlight";
1471 const auto light =
static_cast<SpotLight*
>(baseLight.get());
1472 rpcLight.properties.setProperty({
"position", toArray<3, double>(light->_position)});
1473 rpcLight.properties.setProperty({
"direction", toArray<3, double>(light->_direction)});
1474 rpcLight.properties.setProperty({
"openingAngle", light->_openingAngle});
1475 rpcLight.properties.setProperty({
"penumbraAngle", light->_penumbraAngle});
1476 rpcLight.properties.setProperty({
"radius", light->_radius});
1479 case LightType::AMBIENT:
1481 rpcLight.type =
"ambient";
1486 rpcLight.properties.setProperty({
"color", toArray<3, double>(baseLight->_color)});
1487 rpcLight.properties.setProperty({
"intensity", baseLight->_intensity});
1488 rpcLight.properties.setProperty({
"isVisible", baseLight->_isVisible});
1490 jsonStrings.emplace_back(
to_json(rpcLight));
1500 _handleRPC<SpotLight, int>({METHOD_ADD_LIGHT_SPOT,
"Add a spotlight, returns id",
"light",
1501 "The light and its properties"},
1504 LightManager& lightManager = engine.getScene().getLightManager();
1505 auto light = std::make_shared<SpotLight>(l);
1506 light->_type = LightType::SPOTLIGHT;
1508 const auto id = lightManager.
addLight(light);
1509 engine.triggerRender();
1513 _handleRPC<DirectionalLight, int>({METHOD_ADD_LIGHT_DIRECTIONAL,
"Add a directional light",
"light",
1514 "The light and its properties"},
1517 LightManager& lightManager = engine.getScene().getLightManager();
1518 auto light = std::make_shared<DirectionalLight>(l);
1519 light->_type = LightType::DIRECTIONAL;
1521 const auto id = lightManager.
addLight(light);
1522 engine.triggerRender();
1526 _handleRPC<QuadLight, int>({METHOD_ADD_LIGHT_QUAD,
"Add a quad light",
"light",
"The light and its properties"},
1529 LightManager& lightManager = engine.getScene().getLightManager();
1530 auto light = std::make_shared<QuadLight>(l);
1531 light->_type = LightType::QUAD;
1533 const auto id = lightManager.
addLight(light);
1534 engine.triggerRender();
1538 _handleRPC<SphereLight, int>({METHOD_ADD_LIGHT_SPHERE,
"Add a sphere light",
"light",
1539 "The light and its properties"},
1542 LightManager& lightManager = engine.getScene().getLightManager();
1543 auto light = std::make_shared<SphereLight>(l);
1544 light->_type = LightType::SPHERE;
1546 const auto id = lightManager.
addLight(light);
1547 engine.triggerRender();
1551 _handleRPC<AmbientLight, int>({METHOD_ADD_LIGHT_AMBIENT,
"Add an ambient light",
"light",
1552 "The light and its properties"},
1555 LightManager& lightManager = engine.getScene().getLightManager();
1556 auto light = std::make_shared<AmbientLight>(l);
1557 light->_type = LightType::AMBIENT;
1559 const auto id = lightManager.
addLight(light);
1560 engine.triggerRender();
1568 "Array of light IDs"};
1569 _handleRPC<size_ts, bool>(desc,
1570 [&engine = _engine](
const size_ts& ids)
1572 auto& lightManager = engine.getScene().getLightManager();
1573 for (
const auto id : ids)
1574 lightManager.removeLight(
id);
1575 engine.triggerRender();
1582 const RpcDescription desc{METHOD_CLEAR_LIGHTS,
"Remove all lights in the scene"};
1584 [&engine = _engine]()
1586 auto& lightManager = engine.getScene().getLightManager();
1587 lightManager.clearLights();
1588 engine.triggerRender();
1601 const std::string& sandboxPath = _engine.getParametersManager().getApplicationParameters().getSandboxPath();
1602 size_t pos = path.find(sandboxPath);
1606 ft.
message =
"Path falls outside the sandbox: " + sandboxPath;
1610 if (path.find(
"../") == 0 || path.find(
"/../") != std::string::npos || path.rfind(
"/..") == 4)
1613 ft.
message =
"Illegal path detected";
1618 int err = stat(path.c_str(), &s);
1622 if (s.st_mode & S_IFREG)
1625 FILE* permissionTest = fopen(path.c_str(),
"r");
1626 int openError = errno;
1627 if (permissionTest !=
nullptr)
1628 fclose(permissionTest);
1629 if (openError == EACCES)
1632 ft.
message =
"Permission for " + path +
" denied";
1641 else if (s.st_mode & S_IFDIR)
1644 if (access(path.c_str(), X_OK) < 0)
1646 if (errno == EACCES)
1649 ft.
message =
"Permission for " + path +
" denied";
1661 ft.
type =
"directory";
1665 ft.
message =
"Unknown type of element";
1673 ft.
message =
"Permission for " + path +
" denied";
1688 "Return the type of filer (file or folder) if a given path exists, "
1689 "or none if it does not exists",
1690 "path",
"Absolute path to the filer to check"};
1691 _handleRPC<InputPath, FileType>(desc,
1692 [&](
const auto& inputPath)
1694 this->_rebroadcast(METHOD_FS_EXISTS,
to_json(inputPath),
1695 {_currentClientID});
1696 FileStats fs = this->_getFileStats(inputPath.path);
1699 ft.
error = fs.error;
1708 "Return the content of a file if possible, or an error otherwise",
"path",
1709 "Absolute path to the file"};
1710 _handleRPC<InputPath, FileContent>(desc,
1711 [&](
const auto& inputPath)
1713 this->_rebroadcast(METHOD_FS_GET_CONTENT,
to_json(inputPath),
1714 {_currentClientID});
1716 FileStats ft = this->_getFileStats(inputPath.path);
1719 fc.
error = ft.error;
1723 if (fc.
error == 0 && ft.type ==
"file")
1725 std::ifstream file(inputPath.path);
1729 file.seekg(0, file.end);
1730 long len = file.tellg();
1731 file.seekg(0, file.beg);
1732 std::vector<char> buffer(
static_cast<unsigned long>(len));
1733 file.read(&buffer[0], len);
1735 fc.
content = std::string(buffer.begin(), buffer.end());
1740 fc.
message =
"An unknown error occurred when reading the file " +
1752 "Return the root path of the current "
1753 "execution environment (sandbox)"};
1754 _bindEndpoint(METHOD_FS_GET_ROOT,
1755 [&engine = _engine](
const rockets::jsonrpc::Request&)
1758 fr.
root = engine.getParametersManager().getApplicationParameters().getSandboxPath();
1762 _handleSchema(METHOD_GET_LIGHTS, buildJsonRpcSchemaRequestReturnOnly<FileRoot>(desc));
1768 "Return the content of a file if possible, or an error otherwise",
"path",
1769 "Absolute path to the file"};
1770 _handleRPC<InputPath, DirectoryFileList>(
1772 [&](
const auto& inputPath)
1774 this->_rebroadcast(METHOD_FS_LIST_DIR,
to_json(inputPath), {_currentClientID});
1777 FileStats ft = this->_getFileStats(inputPath.path);
1780 dfl.
error = ft.error;
1784 const bool isDirectory = ft.type ==
"directory";
1785 if (ft.error == 0 && isDirectory)
1789 if ((dir = opendir(inputPath.path.c_str())) !=
nullptr)
1792 std::string slash = inputPath.path[inputPath.path.size() - 1] ==
'/' ?
"" :
"/";
1793 while ((ent = readdir(dir)) !=
nullptr)
1795 const std::string fileName(ent->d_name);
1798 if (fileName ==
"." || fileName ==
"..")
1803 const std::string filePath = inputPath.path + slash + fileName;
1804 FileStats fileStats = this->_getFileStats(filePath);
1806 if (fileStats.
error == 0)
1808 if (fileStats.
type ==
"directory")
1810 dfl.
dirs.push_back(fileName);
1812 else if (fileStats.
type ==
"file")
1816 static_cast<size_t>(fileStats.
sizeBytes) *
static_cast<size_t>(CHAR_BIT);
1817 size_t totalOctets = totalBits / 8;
1828 dfl.
message =
"Unknown error when reading contents of directory " + inputPath.path;
1831 else if (!isDirectory && ft.error == 0)
1834 dfl.
message =
"The path " + inputPath.path +
" is not a directory";
1844 "Add model from remote path; returns model descriptor on success",
1845 Execution::async,
"model_param",
1846 "Model parameters including name, path, transformation, etc."};
1848 auto func = [&](
const ModelParams& modelParams,
const auto)
1849 {
return std::make_shared<AddModelTask>(modelParams, _engine); };
1850 _handleTask<ModelParams, ModelDescriptorPtr>(desc, func);
1856 "Remove the model(s) with the given ID(s) from the scene",
"ids",
1857 "Array of model IDs"};
1858 _handleRPC<size_ts, bool>(desc,
1859 [&engine = _engine](
const size_ts& ids)
1861 for (
const auto id : ids)
1862 engine.getScene().removeModel(
id);
1863 engine.triggerRender();
1870 _bindEndpoint(METHOD_UPDATE_MODEL,
1871 [&engine = _engine](
const rockets::jsonrpc::Request& request)
1874 if (!::
from_json(newDesc, request.message))
1875 return Response::invalidParams();
1877 auto& scene = engine.getScene();
1880 ::from_json(*model, request.message);
1881 scene.markModified();
1882 engine.triggerRender();
1883 return Response{to_json(true)};
1885 return Response{
to_json(
false)};
1888 "Model descriptor"};
1889 _handleSchema(METHOD_UPDATE_MODEL, buildJsonRpcSchemaRequest<ModelDescriptor, bool>(desc));
1894 const RpcParameterDescription desc{METHOD_GET_MODEL_PROPERTIES,
"Get the properties of the given model",
"id",
1898 [&engine = _engine](
const ObjectID& id)
1900 auto model = engine.getScene().getModel(
id.
id);
1902 throw rockets::jsonrpc::response_error(
"Model not found",
1904 return model->getProperties();
1907 _handleSchema(METHOD_GET_MODEL_PROPERTIES, buildJsonRpcSchemaRequest<ObjectID, PropertyMap>(desc));
1913 "param",
"model ID and its properties"};
1915 _bindModelEndpoint(METHOD_SET_MODEL_PROPERTIES,
"properties",
1918 auto props = model->getProperties();
1920 model->setProperties(props);
1924 _handleSchema(METHOD_SET_MODEL_PROPERTIES, buildJsonRpcSchemaRequest<ModelProperties, bool>(desc));
1929 const RpcParameterDescription desc{METHOD_MODEL_PROPERTIES_SCHEMA,
"Get the property schema of the model",
"id",
1930 "ID of the model get its properties schema"};
1932 _jsonrpcServer->bind(METHOD_MODEL_PROPERTIES_SCHEMA,
1933 [&engine = _engine](
const auto& request)
1936 if (::
from_json(modelID, request.message))
1938 auto model = engine.getScene().getModel(modelID.id);
1940 return Response{Response::Error{
"Model not found", MODEL_NOT_FOUND}};
1942 return Response{buildJsonSchema(model->getProperties(),
"ModelProperties")};
1944 return Response::invalidParams();
1947 _handleSchema(METHOD_MODEL_PROPERTIES_SCHEMA, buildJsonRpcSchemaRequest<ObjectID, std::string>(desc));
1953 "Model id and result range"};
1954 _handleRPC<GetInstances, ModelInstances>(
1959 auto& scene = engine.getScene();
1960 auto model = scene.getModel(
id);
1962 throw rockets::jsonrpc::response_error(
"Model not found", MODEL_NOT_FOUND);
1964 const auto& instances = model->getInstances();
1966 std::min(param.
resultRange.y,
unsigned(instances.size()))};
1967 return {instances.begin() + range.x, instances.begin() + range.y};
1973 _handleRPC<std::vector<LoaderInfo>>({METHOD_GET_LOADERS,
"Get all loaders"},
1976 auto& scene = _engine.getScene();
1977 return scene.getLoaderRegistry().getLoaderInfos();
1983 _bindEndpoint(METHOD_UPDATE_INSTANCE,
1984 [&](
const rockets::jsonrpc::Request& request)
1987 if (!::
from_json(newDesc, request.message))
1988 return Response::invalidParams();
1990 auto& scene = _engine.getScene();
1991 auto model = scene.getModel(newDesc.
getModelID());
1993 throw rockets::jsonrpc::response_error(
"Model not found", MODEL_NOT_FOUND);
1995 auto instance = model->getInstance(newDesc.
getInstanceID());
1997 throw rockets::jsonrpc::response_error(
"Instance not found", INSTANCE_NOT_FOUND);
2000 model->getModel().markInstancesDirty();
2001 scene.markModified(
false);
2003 _engine.triggerRender();
2004 _rebroadcast(METHOD_UPDATE_INSTANCE, request.message, {request.clientID});
2006 return Response{
to_json(
true)};
2009 "model_instance",
"Model instance"};
2010 _handleSchema(METHOD_UPDATE_INSTANCE, buildJsonRpcSchemaRequest<ModelInstance, bool>(desc));
2015 const auto requestEndpoint = getRequestEndpointName(endpoint);
2016 const auto notifyEndpoint = getNotificationEndpointName(endpoint);
2018 _jsonrpcServer->bind<
PropertyMap>(requestEndpoint, [&
object = object] {
return object.getPropertyMap(); });
2020 _bindEndpoint(notifyEndpoint,
2021 [&, notifyEndpoint](
const auto& request)
2024 _engine.triggerRender();
2025 this->_rebroadcast(notifyEndpoint, request.message, {request.clientID});
2026 return Response{to_json(true)};
2029 std::vector<std::pair<std::string, PropertyMap>> props;
2030 for (
const auto& type :
object.getTypes())
2031 props.push_back(std::make_pair(type,
object.getPropertyMap(type)));
2035 {requestEndpoint,
"Get the params of the current " + objectName}, props));
2039 "new " + objectName +
" params"};
2043 _handleSchema(endpoint,
buildJsonSchema(props, hyphenatedToCamelCase(endpoint)));
2048 const RpcDescription desc{LOADERS_SCHEMA,
"Get the schema for all loaders"};
2050 _bindEndpoint(LOADERS_SCHEMA,
2051 [&](
const rockets::jsonrpc::Request& )
2053 const auto& loaderInfos = _engine.getScene().getLoaderRegistry().getLoaderInfos();
2055 std::vector<std::pair<std::string, PropertyMap>> props;
2057 props.emplace_back(li.name, li.properties);
2059 return Response{buildJsonSchema(props,
"loaders")};
2067 const RpcParameterDescription desc{METHOD_SET_ENVIRONMENT_MAP,
"Set a environment map in the scene",
"filename",
2068 "environment map texture file"};
2070 _handleRPC<EnvironmentMapParam, bool>(desc,
2071 [&](
const auto& envMap)
2073 this->_rebroadcast(METHOD_SET_ENVIRONMENT_MAP,
to_json(envMap),
2074 {_currentClientID});
2075 if (_engine.getScene().setEnvironmentMap(envMap.filename))
2077 _engine.triggerRender();
2086 const RpcDescription desc{METHOD_GET_ENVIRONMENT_MAP,
"Get the environment map from the scene"};
2088 _handleRPC<EnvironmentMapParam>(desc,
2090 {
return {_engine.getScene().getEnvironmentMap()}; });
2096 "videostream parameters"};
2098 auto action = [&](
const VideoStreamParam& params,
auto clientID,
auto respond,
auto)
2100 if (!_parametersManager.getApplicationParameters().useVideoStreaming())
2102 respond(Response{Response::Error(VIDEOSTREAM_NOT_ENABLED_ERROR)});
2103 return rockets::jsonrpc::CancelRequestCallback();
2106 #ifdef BRAYNS_USE_FFMPEG
2107 this->_rebroadcast(METHOD_SET_VIDEOSTREAM,
to_json(params), {clientID});
2109 const bool changed = params != _videoParams;
2112 respond(Response{
to_json(
false)});
2113 return rockets::jsonrpc::CancelRequestCallback();
2116 _engine.triggerRender();
2117 _videoParams = params;
2118 _videoUpdatedResponse = [respond] { respond(Response{
to_json(
true)}); };
2119 return rockets::jsonrpc::CancelRequestCallback();
2121 respond(Response{Response::Error(VIDEOSTREAM_NOT_SUPPORTED_ERROR)});
2122 return rockets::jsonrpc::CancelRequestCallback();
2125 _handleAsyncRPC<VideoStreamParam, bool>(desc, action);
2130 const RpcDescription desc{METHOD_GET_VIDEOSTREAM,
"Get the videostream parameters"};
2132 _handleRPC<VideoStreamParam>(desc,
2135 #ifdef BRAYNS_USE_FFMPEG
2136 if (!_parametersManager.getApplicationParameters().useVideoStreaming())
2137 throw rockets::jsonrpc::response_error(VIDEOSTREAM_NOT_ENABLED_ERROR);
2138 return _videoParams;
2140 throw rockets::jsonrpc::response_error(VIDEOSTREAM_NOT_SUPPORTED_ERROR);
2147 _controlledStreamingFlag =
true;
2148 _engine.triggerRender();
2153 std::unordered_map<std::string, std::pair<std::mutex, Throttle>>
_throttle;
2156 static constexpr uintptr_t NO_CURRENT_CLIENT{0};
2157 uintptr_t _currentClientID{NO_CURRENT_CLIENT};
2159 #ifdef USE_NETWORKING
2160 std::shared_ptr<uvw::AsyncHandle> _processDelayedNotifies;
2171 bool _manualProcessing{
true};
2176 float _leftover{0.f};
2178 std::map<TaskPtr, std::shared_ptr<async::task<void>>>
_tasks;
2184 bool _endpointsRegistered{
false};
2188 bool _useControlledStream{
false};
2190 std::atomic<bool> _controlledStreamingFlag{
false};
2193 bool _scheduledShutdownActive{
false};
2195 bool _cancelScheduledShutdown{
false};
2206 #ifdef BRAYNS_USE_FFMPEG
2207 std::unique_ptr<Encoder> _encoder;
2209 std::function<void()> _videoUpdatedResponse;
2213 RocketsPlugin::~RocketsPlugin()
2220 _impl = std::make_shared<Impl>(_api);
2221 _api->setActionInterface(_impl);
2224 void RocketsPlugin::preRender()
2228 void RocketsPlugin::postRender()
2230 _impl->postRender();
std::function< std::string(std::string)> RetParamFunc
std::function< void(std::string)> ParamFunc
std::function< void()> VoidFunc
std::function< std::string()> RetFunc
bool useVideoStreaming() const
The Camera class is an abstract interface for a camera in a 3D graphics application....
size_t getID() const
Returns id of this clip plane object.
Provides an abstract implementation of a ray-tracing engine.
Manages light sources in a scene.
PLATFORM_API size_t addLight(LightPtr light)
addLight Attaches a light source to the scene.
The ModelDescriptor struct defines the metadata attached to a model.Model descriptor are exposed via ...
A class representing an instance of a 3D model.
PLATFORM_API size_t getInstanceID() const
Get the value of _instanceID.
PLATFORM_API size_t getModelID() const
Get the value of _modelID.
The ModelParams class represents the parameters needed for initializing a model instance.
PLATFORM_API ApplicationParameters & getApplicationParameters()
void setProperty(const Property &newProperty)
Renderer class inherits from PropertyObject class The Renderer class has methods to render a FrameBuf...
std::unordered_map< std::string, std::string > _schemas
std::unique_ptr< rockets::Server > _rocketsServer
void _handleRemoveLights()
void _bindModelEndpoint(const std::string &method, const std::string &key, std::function< bool(std::string, ModelDescriptorPtr)> action)
std::condition_variable _monitor
void _handleSetVideostream()
void _handleRemoveClipPlanes()
void _handleRPC(const RpcDescription &desc, std::function< void()> action)
void _handleSetModelTransferFunction()
std::unique_ptr< std::thread > _shutDownWorker
void _delayedNotify(const std::function< void()> ¬ify)
void _bindEndpoint(const std::string &method, rockets::jsonrpc::VoidCallback action)
void _handleGetClipPlanes()
void _handleRemoveModel()
std::mutex _scheduleMutex
BinaryRequests _binaryRequests
ParametersManager & _parametersManager
void _handleAnimationParams()
void _handleModelPropertiesSchema()
void _handleTask(const RpcParameterDescription &desc, std::function< std::shared_ptr< Task< R >>(P, uintptr_t)> createTask)
void _handleRPC(const RpcParameterDescription &desc, std::function< void(P)> action)
void _handlePUT(const std::string &endpoint, T &obj, PRE preUpdateFunc, POST postUpdateFunc)
void _bindEndpoint(const std::string &method, rockets::jsonrpc::ResponseCallback action)
void _bindEndpoint(const std::string &method, std::function< RetVal(Params)> action)
void _handleClearLights()
void _triggerControlledStreaming()
void _handleObjectSchema(const std::string &endpoint, T &obj)
void _bindEndpoint(const std::string &method, std::function< void(Params)> action)
void _handleResetCamera()
void _registerEndpoints()
void registerNotification(const RpcDescription &desc, const std::function< void()> &action)
void _handleTriggerImageStream()
FileStats _getFileStats(const std::string &path)
void processDelayedNotifies()
void _handleGetInstances()
void _handleUpdateInstance()
void _handleGetModelTransferFunction()
void _registerRequest(const std::string &name, const RetParamFunc &action)
void _bindEndpoint(const std::string &method, std::function< RetVal()> action)
std::vector< std::function< void()> > _delayedNotifies
void _handleSchema(const std::string &endpoint, const std::string &schema)
void _handleObjectSchema(const std::string &endpoint)
void _handleGetEnvironmentMap()
void registerNotification(const RpcParameterDescription &desc, const PropertyMap &input, const std::function< void(PropertyMap)> &action)
std::mutex _delayedNotifiesMutex
void _broadcastControlledImageJpeg()
std::vector< BaseObject * > _objects
void _registerNotification(const std::string &name, const ParamFunc &action)
void _handlePUT(const std::string &endpoint, T &obj)
ImageGenerator _imageGenerator
void _handlePropertyObject(PropertyObject &object, const std::string &endpoint, const std::string objectName)
void _handleLoadersSchema()
void _rebroadcast(const std::string &endpoint, const std::string &message, const std::set< uintptr_t > &filter)
void _handleUpdateModel()
void _handleFsGetContent()
void _setupRocketsServer()
void registerRequest(const RpcParameterDescription &desc, const PropertyMap &input, const PropertyMap &output, const std::function< PropertyMap(PropertyMap)> &action)
rockets::jsonrpc::Server< rockets::Server > JsonRpcServer
std::unordered_map< std::string, std::pair< std::mutex, Throttle > > _throttle
void _handleGetVideostream()
void _handleSetImageStreamingMode()
void _handleGET(const std::string &endpoint, T &obj, const int64_t throttleTime=DEFAULT_THROTTLE)
std::map< TaskPtr, std::shared_ptr< async::task< void > > > _tasks
void _handleRPC(const RpcDescription &desc, std::function< R()> action)
std::unique_ptr< JsonRpcServer > _jsonrpcServer
void _handle(const std::string &endpoint, T &obj, const int64_t throttleTime=DEFAULT_THROTTLE)
void _handleRPC(const RpcParameterDescription &desc, std::function< R(P)> action)
void _registerNotification(const std::string &name, const VoidFunc &action)
void _handleAsyncRPC(const RpcParameterDescription &desc, std::function< rockets::jsonrpc::CancelRequestCallback(P, uintptr_t, rockets::jsonrpc::AsyncResponse, rockets::jsonrpc::ProgressUpdateCallback)> action)
void registerRequest(const RpcDescription &desc, const PropertyMap &output, const std::function< PropertyMap()> &action)
void _handleUpdateClipPlane()
void _registerRequest(const std::string &name, const RetFunc &action)
void _handleSetModelProperties()
void _handleGetModelProperties()
void _broadcastImageJpeg()
void _handleRequestModelUpload()
bool _endpointsRegistered
void _handleSetEnvironmentMap()
void _handleFsGetListDir()
bool _useControlledStream
void _handleAddClipPlane()
std::string to_json(const core::PropertyMap &obj)
core::PropertyMap jsonToPropertyMap(const std::string &json)
std::string join(const std::vector< std::string > &strings, const std::string &joinWith)
const std::string METHOD_REQUEST_MODEL_UPLOAD
std::string buildJsonRpcSchemaRequestPropertyMaps(const RpcDescription &desc, const std::vector< std::pair< std::string, PropertyMap >> &objs)
std::string buildJsonRpcSchemaNotifyPropertyMaps(const RpcParameterDescription &desc, const std::vector< std::pair< std::string, PropertyMap >> &objs)
bool preUpdate(const std::string &json, PRE preUpdateFunc, typename std::enable_if<!std::is_abstract< T >::value >::type *=0)
std::string buildJsonRpcSchemaNotify(const RpcParameterDescription &desc, P &obj)
std::shared_ptr< ModelDescriptor > ModelDescriptorPtr
bool from_json(T &obj, const std::string &json, PRE preUpdateFunc=[] {}, POST postUpdateFunc=[] {})
std::string buildJsonRpcSchemaRequestReturnOnly(const RpcDescription &desc, R &retVal)
std::string buildJsonSchema(std::vector< std::pair< std::string, PropertyMap >> &objs, const std::string &title)
std::string buildJsonRpcSchemaRequestPropertyMap(const RpcDescription &desc, const PropertyMap &obj)
std::string buildJsonRpcSchemaNotifyPropertyMap(const RpcParameterDescription &desc, const PropertyMap &properties)
std::shared_ptr< ActionInterface > ActionInterfacePtr
glm::vec< 2, uint32_t > Vector2ui
std::vector< ModelInstance > ModelInstances
std::array< double, 4 > Plane
bool preUpdate(const std::string &, PRE, typename std::enable_if< std::is_abstract< T >::value >::type *=0)
void init(core::Box< U > *, ObjectHandler *)
ScopedCurrentClient(uintptr_t ¤tClientID, const uintptr_t newID)