# HG changeset patch # User Benjamin Golinvaux # Date 1551863699 -3600 # Node ID 7105a0bad25000eb49ce1b1f1a0e2a91e21d839f # Parent ce49eae4c8875954a4ea38b9636c68575ecec237 - Added HandleSerializedMessage to IStoneApplication (empty impl) - Split UpdateWebApplication with "WithString" and "WithSerializedMessage" variants - Due to the modules in TS, globals are now unallowed and the callbacks from C++ to JS are stored in the "window" instance - Split UpdateStoneApplicationStatusFromCpp with "WithString" and "WithSerializedMessage" variants - Split NotifyStatusUpdateFromCppToWeb with "WithString" and "WithSerializedMessage" variants - SendMessageToStoneApplication (C++ global) has been split into SendSerializedMessageToStoneApplication and SendCommandToStoneApplication - In WasmPlatformApplicationAdapter: HandleMessageFromWeb becomes HandleCommandFromWeb - In WasmPlatformApplicationAdapter: added HandleSerializedMessageFromWeb - stonegentool now handles the "json" primitive type (used, a.o., in the VSOL "EditInstance" message) - Fixed indentation and added json serialization overloads in the stonegentool templates - Added test of the json primitive type to testWasmIntegrated (in Resources/CodeGeneration) - Adapted testWasmIntegrated (in Resources/CodeGeneration) to the changes above diff -r ce49eae4c887 -r 7105a0bad250 Applications/IStoneApplication.h --- a/Applications/IStoneApplication.h Fri Mar 01 16:18:38 2019 +0100 +++ b/Applications/IStoneApplication.h Wed Mar 06 10:14:59 2019 +0100 @@ -66,6 +66,8 @@ virtual BaseCommandBuilder& GetCommandBuilder() = 0; + virtual void HandleSerializedMessage(const char* data) {}; + virtual void ExecuteCommand(ICommand& command) { } diff -r ce49eae4c887 -r 7105a0bad250 Applications/Samples/SimpleViewer/Wasm/simple-viewer.ts --- a/Applications/Samples/SimpleViewer/Wasm/simple-viewer.ts Fri Mar 01 16:18:38 2019 +0100 +++ b/Applications/Samples/SimpleViewer/Wasm/simple-viewer.ts Wed Mar 06 10:14:59 2019 +0100 @@ -59,8 +59,8 @@ // this method is called "from the C++ code" when the StoneApplication is updated. // it can be used to update the UI of the application -function UpdateWebApplication(statusUpdateMessageString: string) { - console.log("updating web application: ", statusUpdateMessageString); +function UpdateWebApplicationWithString(statusUpdateMessageString: string) { + console.log("updating web application with string: ", statusUpdateMessageString); let statusUpdateMessage = JSON.parse(statusUpdateMessageString); if ("event" in statusUpdateMessage) { @@ -70,3 +70,12 @@ } } } + +function UpdateWebApplicationWithSerializedMessage(statusUpdateMessageString: string) { + console.log("updating web application with serialized message: ", statusUpdateMessageString); + console.log(""); +} + +// make it available to other js scripts in the application +( window).UpdateWebApplicationWithString = UpdateWebApplicationWithString; +( window).UpdateWebApplicationWithSerializedMessage = UpdateWebApplicationWithSerializedMessage; diff -r ce49eae4c887 -r 7105a0bad250 Applications/Samples/SimpleViewerApplicationSingleFile.h --- a/Applications/Samples/SimpleViewerApplicationSingleFile.h Fri Mar 01 16:18:38 2019 +0100 +++ b/Applications/Samples/SimpleViewerApplicationSingleFile.h Wed Mar 06 10:14:59 2019 +0100 @@ -205,7 +205,12 @@ { } - virtual void HandleMessageFromWeb(std::string& output, const std::string& input) + virtual void HandleSerializedMessageFromWeb(std::string& output, const std::string& input) + { + // the simple viewer does not use the serialized messages facilities + } + + virtual void HandleCommandFromWeb(std::string& output, const std::string& input) { if (input == "select-tool:line-measure") { @@ -221,9 +226,14 @@ output = "ok"; } + virtual void NotifySerializedMessageFromCppToWeb(const std::string& statusUpdateMessage) + { + UpdateStoneApplicationStatusFromCppWithSerializedMessage(statusUpdateMessage.c_str()); + } + virtual void NotifyStatusUpdateFromCppToWeb(const std::string& statusUpdateMessage) { - UpdateStoneApplicationStatusFromCpp(statusUpdateMessage.c_str()); + UpdateStoneApplicationStatusFromCppWithString(statusUpdateMessage.c_str()); } }; diff -r ce49eae4c887 -r 7105a0bad250 Applications/Samples/Web/simple-viewer-single-file.ts --- a/Applications/Samples/Web/simple-viewer-single-file.ts Fri Mar 01 16:18:38 2019 +0100 +++ b/Applications/Samples/Web/simple-viewer-single-file.ts Wed Mar 06 10:14:59 2019 +0100 @@ -42,10 +42,20 @@ // this method is called "from the C++ code" when the StoneApplication is updated. // it can be used to update the UI of the application -function UpdateWebApplication(statusUpdateMessage: string) { +function UpdateWebApplicationWithString(statusUpdateMessage: string) { console.log(statusUpdateMessage); if (statusUpdateMessage.startsWith("series-description=")) { document.getElementById("series-description").innerText = statusUpdateMessage.split("=")[1]; } } + +function UpdateWebApplicationWithSerializedMessage(statusUpdateMessageString: string) { + console.log("updating web application with serialized message: ", statusUpdateMessageString); + console.log(""); +} + +// make it available to other js scripts in the application +( window).UpdateWebApplicationWithString = UpdateWebApplicationWithString; + +( window).UpdateWebApplicationWithSerializedMessage = UpdateWebApplicationWithSerializedMessage; diff -r ce49eae4c887 -r 7105a0bad250 Platforms/Wasm/Defaults.cpp --- a/Platforms/Wasm/Defaults.cpp Fri Mar 01 16:18:38 2019 +0100 +++ b/Platforms/Wasm/Defaults.cpp Wed Mar 06 10:14:59 2019 +0100 @@ -342,20 +342,37 @@ viewport->MouseLeave(); } - const char* EMSCRIPTEN_KEEPALIVE SendMessageToStoneApplication(const char* message) + const char* EMSCRIPTEN_KEEPALIVE SendSerializedMessageToStoneApplication(const char* message) { static std::string output; // we don't want the string to be deallocated when we return to JS code so we always use the same string (this is fine since JS is single-thread) - printf("SendMessageToStoneApplication (JS -> C++): %s\n", message); + printf("SendSerializedMessageToStoneApplication\n"); + printf("%s", message); if (applicationWasmAdapter.get() != NULL) { - applicationWasmAdapter->HandleMessageFromWeb(output, std::string(message)); + printf("sending serialized message to C++\n"); + applicationWasmAdapter->HandleSerializedMessageFromWeb(output, std::string(message)); return output.c_str(); } - printf("This stone application does not have a Web Adapter\n"); + printf("This Stone application does not have a Web Adapter"); return NULL; } + const char* EMSCRIPTEN_KEEPALIVE SendCommandToStoneApplication(const char* message) + { + static std::string output; // we don't want the string to be deallocated when we return to JS code so we always use the same string (this is fine since JS is single-thread) + + printf("SendCommandToStoneApplication\n"); + printf("%s", message); + + if (applicationWasmAdapter.get() != NULL) { + printf("sending command to C++\n"); + applicationWasmAdapter->HandleCommandFromWeb(output, std::string(message)); + return output.c_str(); + } + printf("This Stone application does not have a Web Adapter"); + return NULL; + } #ifdef __cplusplus } diff -r ce49eae4c887 -r 7105a0bad250 Platforms/Wasm/Defaults.h --- a/Platforms/Wasm/Defaults.h Fri Mar 01 16:18:38 2019 +0100 +++ b/Platforms/Wasm/Defaults.h Wed Mar 06 10:14:59 2019 +0100 @@ -16,7 +16,8 @@ // JS methods accessible from C++ extern void ScheduleWebViewportRedrawFromCpp(ViewportHandle cppViewportHandle); - extern void UpdateStoneApplicationStatusFromCpp(const char* statusUpdateMessage); + extern void UpdateStoneApplicationStatusFromCppWithString(const char* statusUpdateMessage); + extern void UpdateStoneApplicationStatusFromCppWithSerializedMessage(const char* statusUpdateMessage); // C++ methods accessible from JS extern void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle cppViewportHandle); diff -r ce49eae4c887 -r 7105a0bad250 Platforms/Wasm/WasmPlatformApplicationAdapter.cpp --- a/Platforms/Wasm/WasmPlatformApplicationAdapter.cpp Fri Mar 01 16:18:38 2019 +0100 +++ b/Platforms/Wasm/WasmPlatformApplicationAdapter.cpp Wed Mar 06 10:14:59 2019 +0100 @@ -8,44 +8,83 @@ namespace OrthancStone { - WasmPlatformApplicationAdapter::WasmPlatformApplicationAdapter(MessageBroker& broker, IStoneApplication& application) - : IObserver(broker), - application_(application) + WasmPlatformApplicationAdapter::WasmPlatformApplicationAdapter(MessageBroker& broker, IStoneApplication& application) + : IObserver(broker), + application_(application) + { + } + + void WasmPlatformApplicationAdapter::HandleCommandFromWeb(std::string& output, const std::string& input) + { + try { - } - - void WasmPlatformApplicationAdapter::HandleMessageFromWeb(std::string& output, const std::string& input) - { - try + Json::Value inputJson; + // if the message is a command, build it and execute it + if (MessagingToolbox::ParseJson(inputJson, input.c_str(), input.size())) { - Json::Value inputJson; - // if the message is a command, build it and execute it - if (MessagingToolbox::ParseJson(inputJson, input.c_str(), input.size())) - { - std::unique_ptr command(application_.GetCommandBuilder().CreateFromJson(inputJson)); - if (command.get() == NULL) - printf("Could not parse command: '%s'\n", input.c_str()); - else - application_.ExecuteCommand(*command); - } - } - catch (StoneException& exc) - { - printf("Error while handling message from web (error code = %d):\n", exc.GetErrorCode()); - printf("While interpreting input: '%s'\n", input.c_str()); - } + std::unique_ptr command(application_.GetCommandBuilder().CreateFromJson(inputJson)); + if (command.get() == NULL) + printf("Could not parse command: '%s'\n", input.c_str()); + else + application_.ExecuteCommand(*command); + } + } + catch (StoneException& exc) + { + printf("Error while handling command from web (error code = %d):\n", exc.GetErrorCode()); + printf("While interpreting input: '%s'\n", input.c_str()); + output = std::string("ERROR : "); + } + catch (std::exception& exc) + { + printf("Error while handling message from web (error text = %s):\n", exc.what()); + printf("While interpreting input: '%s'\n", input.c_str()); + output = std::string("ERROR : "); + } + } + + void WasmPlatformApplicationAdapter::HandleSerializedMessageFromWeb(std::string& output, const std::string& input) + { + try + { + application_.HandleSerializedMessage(input.c_str()); } + catch (StoneException& exc) + { + printf("Error while handling message from web (error code = %d):\n", exc.GetErrorCode()); + printf("While interpreting input: '%s'\n", input.c_str()); + output = std::string("ERROR : "); + } + catch (std::exception& exc) + { + printf("Error while handling message from web (error text = %s):\n", exc.what()); + printf("While interpreting input: '%s'\n", input.c_str()); + output = std::string("ERROR : "); + } + } - void WasmPlatformApplicationAdapter::NotifyStatusUpdateFromCppToWeb(const std::string& statusUpdateMessage) + void WasmPlatformApplicationAdapter::NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage) + { + try { - try - { - UpdateStoneApplicationStatusFromCpp(statusUpdateMessage.c_str()); - } - catch (...) - { - printf("Error while handling message to web\n"); - } + UpdateStoneApplicationStatusFromCppWithString(statusUpdateMessage.c_str()); + } + catch (...) + { + printf("Error while handling string message to web\n"); } + } + + void WasmPlatformApplicationAdapter::NotifyStatusUpdateFromCppToWebWithSerializedMessage(const std::string& statusUpdateMessage) + { + try + { + UpdateStoneApplicationStatusFromCppWithSerializedMessage(statusUpdateMessage.c_str()); + } + catch (...) + { + printf("Error while handling serialized message to web\n"); + } + } } \ No newline at end of file diff -r ce49eae4c887 -r 7105a0bad250 Platforms/Wasm/WasmPlatformApplicationAdapter.h --- a/Platforms/Wasm/WasmPlatformApplicationAdapter.h Fri Mar 01 16:18:38 2019 +0100 +++ b/Platforms/Wasm/WasmPlatformApplicationAdapter.h Wed Mar 06 10:14:59 2019 +0100 @@ -12,7 +12,9 @@ public: WasmPlatformApplicationAdapter(MessageBroker& broker, IStoneApplication& application); - virtual void HandleMessageFromWeb(std::string& output, const std::string& input); - virtual void NotifyStatusUpdateFromCppToWeb(const std::string& statusUpdateMessage); + virtual void HandleSerializedMessageFromWeb(std::string& output, const std::string& input); + virtual void HandleCommandFromWeb(std::string& output, const std::string& input); + virtual void NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage); + virtual void NotifyStatusUpdateFromCppToWebWithSerializedMessage(const std::string& statusUpdateMessage); }; } \ No newline at end of file diff -r ce49eae4c887 -r 7105a0bad250 Platforms/Wasm/default-library.js --- a/Platforms/Wasm/default-library.js Fri Mar 01 16:18:38 2019 +0100 +++ b/Platforms/Wasm/default-library.js Wed Mar 06 10:14:59 2019 +0100 @@ -7,9 +7,15 @@ CreateWasmViewportFromCpp: function(htmlCanvasId) { return window.CreateWasmViewport(htmlCanvasId); }, - // each time the StoneApplication updates its status, it may signal it through this method. i.e, to change the status of a button in the web interface - UpdateStoneApplicationStatusFromCpp: function(statusUpdateMessage) { + // each time the StoneApplication updates its status, it may signal it + // through this method. i.e, to change the status of a button in the web interface + UpdateStoneApplicationStatusFromCppWithString: function(statusUpdateMessage) { var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage); - UpdateWebApplication(statusUpdateMessage_); + window.UpdateWebApplicationWithString(statusUpdateMessage_); + }, + // same, but with a serialized message + UpdateStoneApplicationStatusFromCppWithSerializedMessage: function(statusUpdateMessage) { + var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage); + window.UpdateWebApplicationWithSerializedMessage(statusUpdateMessage_); } }); diff -r ce49eae4c887 -r 7105a0bad250 Platforms/Wasm/wasm-application-runner.ts --- a/Platforms/Wasm/wasm-application-runner.ts Fri Mar 01 16:18:38 2019 +0100 +++ b/Platforms/Wasm/wasm-application-runner.ts Wed Mar 06 10:14:59 2019 +0100 @@ -19,7 +19,8 @@ export var CreateCppViewport: Function = null; var ReleaseCppViewport: Function = null; var StartWasmApplication: Function = null; -export var SendMessageToStoneApplication: Function = null; +export var SendSerializedMessageToStoneApplication: Function = null; +export var SendCommandToStoneApplication: Function = null; function DoAnimationThread() { if (WasmDoAnimation != null) { @@ -99,7 +100,8 @@ // no need to put this into the globals for it's only used in this very module WasmDoAnimation = ( window).StoneFrameworkModule.cwrap('WasmDoAnimation', null, []); - SendMessageToStoneApplication = ( window).StoneFrameworkModule.cwrap('SendMessageToStoneApplication', 'string', ['string']); + SendSerializedMessageToStoneApplication = ( window).StoneFrameworkModule.cwrap('SendSerializedMessageToStoneApplication', 'string', ['string']); + SendCommandToStoneApplication = ( window).StoneFrameworkModule.cwrap('SendCommandToStoneApplication', 'string', ['string']); console.log("Connecting C++ methods to JS methods - done"); diff -r ce49eae4c887 -r 7105a0bad250 Resources/CodeGeneration/stonegentool.py --- a/Resources/CodeGeneration/stonegentool.py Fri Mar 01 16:18:38 2019 +0100 +++ b/Resources/CodeGeneration/stonegentool.py Wed Mar 06 10:14:59 2019 +0100 @@ -110,6 +110,7 @@ retVal = retVal.replace("int32", "int32_t") retVal = retVal.replace("float32", "float") retVal = retVal.replace("float64", "double") + retVal = retVal.replace("json", "Json::Value") return retVal def CanonToTs(canonicalTypename): @@ -125,6 +126,7 @@ retVal = retVal.replace("float32", "number") retVal = retVal.replace("float64", "number") retVal = retVal.replace("bool", "boolean") + retVal = retVal.replace("json", "Object") return retVal def NeedsTsConstruction(enums, tsType): diff -r ce49eae4c887 -r 7105a0bad250 Resources/CodeGeneration/template.in.h --- a/Resources/CodeGeneration/template.in.h Fri Mar 01 16:18:38 2019 +0100 +++ b/Resources/CodeGeneration/template.in.h Wed Mar 06 10:14:59 2019 +0100 @@ -38,6 +38,16 @@ return result; } + inline void _StoneDeserializeValue(Json::Value& destValue, const Json::Value& jsonValue) + { + destValue = jsonValue; + } + + inline Json::Value _StoneSerializeValue(Json::Value value) + { + return value; + } + /** Throws in case of problem */ inline void _StoneDeserializeValue(double& destValue, const Json::Value& jsonValue) { @@ -240,7 +250,12 @@ { return std::string("{{key}}"); } -{%endfor%}; +{%endfor%} std::stringstream ss; + ss << "Value \"" << value << "\" cannot be converted to {{enum['name']}}. Possible values are: " +{% for key in enum['fields']%} << " {{key}} = " << static_cast({{enum['name']}}_{{key}}) << ", " +{% endfor %} << std::endl; + std::string msg = ss.str(); + throw std::runtime_error(msg); } inline void FromString({{enum['name']}}& value, std::string strValue) diff -r ce49eae4c887 -r 7105a0bad250 Resources/CodeGeneration/template.in.ts --- a/Resources/CodeGeneration/template.in.ts Fri Mar 01 16:18:38 2019 +0100 +++ b/Resources/CodeGeneration/template.in.ts Wed Mar 06 10:14:59 2019 +0100 @@ -44,25 +44,12 @@ }; {%endfor%} -{% for struct in structs%} export class {{struct['name']}} { - {% if struct %} - {% if struct['fields'] %} - {% for key in struct['fields']%} - {{key}}:{{CanonToTs(struct['fields'][key])}}; - {% endfor %} - {% endif %} - {% endif %} +{% for struct in structs%}export class {{struct['name']}} { +{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} {{key}}:{{CanonToTs(struct['fields'][key])}}; +{% endfor %}{% endif %}{% endif %} constructor() { - {% if struct %} - {% if struct['fields'] %} - {% for key in struct['fields']%} - {% if NeedsTsConstruction(enums,CanonToTs(struct['fields'][key])) %} - this.{{key}} = new {{CanonToTs(struct['fields'][key])}}(); - {% endif %} - {% endfor %} - {% endif %} - {% endif %} -} +{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}{% if NeedsTsConstruction(enums,CanonToTs(struct['fields'][key])) %} this.{{key}} = new {{CanonToTs(struct['fields'][key])}}(); +{% endif %}{% endfor %}{% endif %}{% endif %} } public StoneSerialize(): string { let container: object = {}; diff -r ce49eae4c887 -r 7105a0bad250 Resources/CodeGeneration/testWasmIntegrated/main.cpp --- a/Resources/CodeGeneration/testWasmIntegrated/main.cpp Fri Mar 01 16:18:38 2019 +0100 +++ b/Resources/CodeGeneration/testWasmIntegrated/main.cpp Wed Mar 06 10:14:59 2019 +0100 @@ -8,6 +8,45 @@ int main() { std::cout << "Hello world from testWasmIntegrated! (this is sent from C++)" << std::endl; + try + { + const char* jsonData = R"bgo({"definition": + { + "val" : [ "berk", 42 ], + "zozo" : { "23": "zloutch", "lalala": 42} + } + })bgo"; + std::string strValue(jsonData); + + Json::Value readValue; + + Json::CharReaderBuilder builder; + Json::CharReader* reader = builder.newCharReader(); + + StoneSmartPtr ptr(reader); + + std::string errors; + + bool ok = reader->parse( + strValue.c_str(), + strValue.c_str() + strValue.size(), + &readValue, + &errors + ); + if (!ok) + { + std::stringstream ss; + ss << "Jsoncpp parsing error: " << errors; + throw std::runtime_error(ss.str()); + } + std::cout << "Json parsing OK" << std::endl; + std::cout << readValue << std::endl; + } + catch(std::exception& e) + { + std::cout << "Json parsing THROW" << std::endl; + std::cout << "e.what() = " << e.what() << std::endl; + } } extern "C" void SendMessageFromCppJS(const char* message); diff -r ce49eae4c887 -r 7105a0bad250 Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.ts --- a/Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.ts Fri Mar 01 16:18:38 2019 +0100 +++ b/Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.ts Wed Mar 06 10:14:59 2019 +0100 @@ -57,6 +57,7 @@ struct C: someBs: vector ddd: vector + definition: vector struct A: someStrings: vector @@ -76,6 +77,7 @@ titi: map lulu: map movieType: MovieType + definition: json enum MovieType: - RomCom @@ -142,7 +144,16 @@ [ "Mercadet", "Poisson" - ] + ], + "definition": + { + "val" : [ "berk", 42 ], + "zozo" : + { + "23": "zloutch", + "lalala": 42 + } + } } }`; stockSerializedMessages["Test 2"] = ` { @@ -224,8 +235,8 @@ // this method is called "from the C++ code" when the StoneApplication is updated. // it can be used to update the UI of the application -function UpdateWebApplication(statusUpdateMessageString: string) { - console.log("updating web application: ", statusUpdateMessageString); +function UpdateWebApplicationWithString(statusUpdateMessageString: string) { + console.log("updating web application (string): ", statusUpdateMessageString); let statusUpdateMessage = JSON.parse(statusUpdateMessageString); if ("event" in statusUpdateMessage) @@ -237,3 +248,10 @@ } } } + + +function UpdateWebApplicationWithSerializedMessage(statusUpdateMessageString: string) { + console.log("updating web application (serialized message): ", statusUpdateMessageString); + console.log(""); +} + \ No newline at end of file diff -r ce49eae4c887 -r 7105a0bad250 Resources/CodeGeneration/testWasmIntegrated/testWasmIntegratedCpp_api.yaml --- a/Resources/CodeGeneration/testWasmIntegrated/testWasmIntegratedCpp_api.yaml Fri Mar 01 16:18:38 2019 +0100 +++ b/Resources/CodeGeneration/testWasmIntegrated/testWasmIntegratedCpp_api.yaml Wed Mar 06 10:14:59 2019 +0100 @@ -11,6 +11,7 @@ struct C: someBs: vector ddd: vector + definition: vector struct A: someStrings: vector @@ -30,6 +31,7 @@ titi: map lulu: map movieType: MovieType + definition: json enum MovieType: - RomCom