diff --git a/ModeliRpc/ModeliRpcCLR/LogEventArgs.cpp b/ModeliRpc/ModeliRpcCLR/LogEventArgs.cpp new file mode 100644 index 0000000000000000000000000000000000000000..176149a1f803533f6bf64d98b6a9ca7dab997c5b --- /dev/null +++ b/ModeliRpc/ModeliRpcCLR/LogEventArgs.cpp @@ -0,0 +1,7 @@ +#include "stdafx.h" +#include "LogEventArgs.h" + + +LogEventArgs::LogEventArgs() +{ +} diff --git a/ModeliRpc/ModeliRpcCLR/LogEventArgs.h b/ModeliRpc/ModeliRpcCLR/LogEventArgs.h new file mode 100644 index 0000000000000000000000000000000000000000..1b53d3392a3fd39a5471cdf071598d9a6cd16a85 --- /dev/null +++ b/ModeliRpc/ModeliRpcCLR/LogEventArgs.h @@ -0,0 +1,12 @@ +#pragma once +using namespace System; + +ref class LogEventArgs : public EventArgs +{ +public: + LogEventArgs(); + property String^ InstanceName; + property int Status; + property String^ Message; +}; + diff --git a/ModeliRpc/ModeliRpcCLR/ModeliRpc.cpp b/ModeliRpc/ModeliRpcCLR/ModeliRpc.cpp index c8329dd6912d1df86793e5aaafc1e7f1afc48476..883e0352a13d4e032490cf8fd4240d460462988f 100644 --- a/ModeliRpc/ModeliRpcCLR/ModeliRpc.cpp +++ b/ModeliRpc/ModeliRpcCLR/ModeliRpc.cpp @@ -5,13 +5,13 @@ using namespace msclr::interop; using namespace System::Runtime::InteropServices; -namespace ModeliRpcImpl +namespace ModeliRpc { - ModeliRpcImpl::ModeliRpcImpl(String^ address, int port) + FrontendRpc::FrontendRpc(String^ address, int port) { // Managed callbacks - _newValues = gcnew NewValuesDelegate(this, &ModeliRpcImpl::onNewValues); - _log = gcnew LogDelegate(this, &ModeliRpcImpl::onLog); + _newValues = gcnew NewValuesDelegate(this, &FrontendRpc::onNewValues); + _log = gcnew LogDelegate(this, &FrontendRpc::onLog); // Load the unmanged instance HINSTANCE _rpcDll = LoadLibrary("ModeliRpcNative.dll"); if (!_rpcDll) @@ -34,12 +34,12 @@ namespace ModeliRpcImpl } - ModeliRpcImpl::~ModeliRpcImpl() + FrontendRpc::~FrontendRpc() { // Call the finalizer - this->!ModeliRpcImpl(); + this->!FrontendRpc(); } - ModeliRpcImpl::!ModeliRpcImpl() + FrontendRpc::!FrontendRpc() { // Clear all unmanaged resources if (_rpcFrontend) @@ -52,16 +52,54 @@ namespace ModeliRpcImpl } } - void ModeliRpcImpl::onNewValues(double timestamp, ValuesStruct values) + void FrontendRpc::onNewValues(double timestamp, ValuesStruct values) { - throw gcnew System::NotImplementedException(); + // Copy data into the EventArgs + auto args = gcnew NewValuesEventArgs(); + args->Timestamp = timestamp; + args->InstanceName = marshal_as<String^> (values.instanceName); + // Int + args->IntegerValueRefs = gcnew array<unsigned int>(values.integerValueRefs.size()); + pin_ptr<unsigned int> pinIntegerVrs = &args->IntegerValueRefs[0]; + memcpy_s(pinIntegerVrs, args->IntegerValueRefs->Length, values.integerValueRefs.data(), values.integerValueRefs.size()); + args->IntegerValues = gcnew array<int>(values.integerValues.size()); + pin_ptr<int> pinIntegerValues = &args->IntegerValues[0]; + memcpy_s(pinIntegerValues, args->IntegerValues->Length, values.integerValues.data(), values.integerValues.size()); + // Real + args->RealValueRefs = gcnew array<unsigned int>(values.realValueRefs.size()); + pin_ptr<unsigned int> pinRealVrs = &args->RealValueRefs[0]; + memcpy_s(pinRealVrs, args->RealValueRefs->Length, values.realValueRefs.data(), values.realValueRefs.size()); + args->RealValues = gcnew array<double>(values.realValues.size()); + pin_ptr<double> pinRealValues = &args->RealValues[0]; + memcpy_s(pinIntegerValues, args->RealValues->Length, values.realValues.data(), values.realValues.size()); + // Bool + args->BoolValueRefs = gcnew array<unsigned int>(values.boolValueRefs.size()); + pin_ptr<unsigned int> pinBoolVrs = &args->BoolValueRefs[0]; + memcpy_s(pinBoolVrs, args->BoolValueRefs->Length, values.boolValueRefs.data(), values.boolValueRefs.size()); + args->BoolValues = gcnew array<bool>(values.boolValues.size()); + for (int i = 0; i < args->BoolValues->Length; i++) + { + args->BoolValues[i] = values.boolValues[i]; + } + // String + args->StringValueRefs = gcnew array<unsigned int>(values.stringValueRefs.size()); + pin_ptr<unsigned int> pinStringVrs = &args->StringValueRefs[0]; + memcpy_s(pinStringVrs, args->StringValueRefs->Length, values.stringValueRefs.data(), values.stringValueRefs.size()); + args->StringValues = gcnew array<String^>(values.stringValues.size()); + for (int i = 0; i < args->StringValues->Length; i++) + { + args->StringValues[i] = marshal_as<String^>(values.stringValues[i]); + } + // Fire! + NewValuesArrived(this, args); } - - void ModeliRpcImpl::onLog(std::string instanceName, fmi2Status status, std::string message) + + void FrontendRpc::onLog(std::string instanceName, int status, std::string message) { - throw gcnew System::NotImplementedException(); + auto args = gcnew LogEventArgs(); + args->InstanceName = marshal_as<String^>(instanceName); + args->Status = status; + args->Message = marshal_as<String^>(message); + LogArrived(this, args); } - - - } \ No newline at end of file diff --git a/ModeliRpc/ModeliRpcCLR/ModeliRpc.h b/ModeliRpc/ModeliRpcCLR/ModeliRpc.h index c7a1f9d5c4625a38128a85726c1a3420a43d0679..ce5677b2abfe77ca938ac73a38babf8cfaac7cb7 100644 --- a/ModeliRpc/ModeliRpcCLR/ModeliRpc.h +++ b/ModeliRpc/ModeliRpcCLR/ModeliRpc.h @@ -1,10 +1,13 @@ #pragma once -#include"../ModeliRpcNative/IRpcFrontend.h" -#include <string> +// Faster build by not incuding all of windows #ifndef VC_EXTRALEAN #define VC_EXTRALEAN #endif -#include <windows.h> +#include <windows.h> // Always on top otherwise build fails (IServicProvider CLI issue) +#include "../ModeliRpcNative/IRpcFrontend.h" +#include <string> +#include "NewValuesEventArgs.h" +#include "LogEventArgs.h" using namespace System; @@ -15,29 +18,35 @@ namespace capnp class EzRpcClient; } -namespace ModeliRpcImpl +namespace ModeliRpc { - ref class ModeliRpcImpl + ref class FrontendRpc { private: // Unmanaged rpc interface - IRpcFrontend* _rpcFrontend; + IRpcFrontend * _rpcFrontend; // The native project exports a dll to hide the rpc framework HMODULE _rpcDll; // Delegates for callbacks delegate void NewValuesDelegate(double timestamp, ValuesStruct values); - delegate void LogDelegate(std::string instanceName, fmi2Status status, std::string message); + delegate void LogDelegate(std::string instanceName, int status, std::string message); NewValuesDelegate^ _newValues; LogDelegate^ _log; // Functions for the delegates void onNewValues(double timestamp, ValuesStruct values); - void onLog(std::string instanceName, fmi2Status status, std::string message); + void onLog(std::string instanceName, int status, std::string message); public: - ModeliRpcImpl(String^ address, int port); - ~ModeliRpcImpl(); // Destructor (IDisposable) - !ModeliRpcImpl(); // Finalizer + /// Create a new managed instance for RPCs to the ModeliBackend. + FrontendRpc(String^ address, int port); + ~FrontendRpc(); // Destructor (IDisposable) + !FrontendRpc(); // Finalizer + + /// Gets fired when new values arrived from the backend. + event EventHandler<NewValuesEventArgs^>^ NewValuesArrived; + /// Gets fired when logging messages arrive from the backend. + event EventHandler<LogEventArgs^>^ LogArrived; }; } \ No newline at end of file diff --git a/ModeliRpc/ModeliRpcCLR/ModeliRpcCLR.vcxproj b/ModeliRpc/ModeliRpcCLR/ModeliRpcCLR.vcxproj index ab51d943bee78e7bf6991321d8f58104df52e0bc..75e5902823262a492c0dbc5d164b2614a01452c8 100644 --- a/ModeliRpc/ModeliRpcCLR/ModeliRpcCLR.vcxproj +++ b/ModeliRpc/ModeliRpcCLR/ModeliRpcCLR.vcxproj @@ -21,7 +21,7 @@ <PropertyGroup Label="Globals"> <VCProjectVersion>15.0</VCProjectVersion> <ProjectGuid>{A8187A41-1D24-438F-9B5D-0F7BF270130E}</ProjectGuid> - <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> + <TargetFrameworkVersion>v4.7</TargetFrameworkVersion> <Keyword>ManagedCProj</Keyword> <RootNamespace>ModeliRpcCLR</RootNamespace> <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion> @@ -144,13 +144,17 @@ <Reference Include="System.Xml" /> </ItemGroup> <ItemGroup> + <ClInclude Include="LogEventArgs.h" /> <ClInclude Include="ModeliRpc.h" /> + <ClInclude Include="NewValuesEventArgs.h" /> <ClInclude Include="resource.h" /> <ClInclude Include="Stdafx.h" /> </ItemGroup> <ItemGroup> <ClCompile Include="AssemblyInfo.cpp" /> + <ClCompile Include="LogEventArgs.cpp" /> <ClCompile Include="ModeliRpc.cpp" /> + <ClCompile Include="NewValuesEventArgs.cpp" /> <ClCompile Include="Stdafx.cpp"> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader> @@ -167,11 +171,6 @@ <ItemGroup> <Image Include="app.ico" /> </ItemGroup> - <ItemGroup> - <ProjectReference Include="..\ModeliRpcNative\ModeliRpcNative.vcxproj"> - <Project>{171ed599-c0c6-4342-8328-e7abe7f7feaa}</Project> - </ProjectReference> - </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> diff --git a/ModeliRpc/ModeliRpcCLR/ModeliRpcCLR.vcxproj.filters b/ModeliRpc/ModeliRpcCLR/ModeliRpcCLR.vcxproj.filters index 1a04e777243f92a1a9f00dd44c161294c7f27a82..d71ba5a0da4f4ff0a30aab49b833f2109fa686ed 100644 --- a/ModeliRpc/ModeliRpcCLR/ModeliRpcCLR.vcxproj.filters +++ b/ModeliRpc/ModeliRpcCLR/ModeliRpcCLR.vcxproj.filters @@ -24,6 +24,12 @@ <ClInclude Include="ModeliRpc.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="NewValuesEventArgs.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="LogEventArgs.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="AssemblyInfo.cpp"> @@ -35,6 +41,12 @@ <ClCompile Include="ModeliRpc.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="NewValuesEventArgs.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="LogEventArgs.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <Text Include="ReadMe.txt" /> diff --git a/ModeliRpc/ModeliRpcCLR/NewValuesEventArgs.cpp b/ModeliRpc/ModeliRpcCLR/NewValuesEventArgs.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4f59c5671758c283f5043e67e3fefbc1a283b5f9 --- /dev/null +++ b/ModeliRpc/ModeliRpcCLR/NewValuesEventArgs.cpp @@ -0,0 +1,7 @@ +#include "stdafx.h" +#include "NewValuesEventArgs.h" + + +NewValuesEventArgs::NewValuesEventArgs() +{ +} diff --git a/ModeliRpc/ModeliRpcCLR/NewValuesEventArgs.h b/ModeliRpc/ModeliRpcCLR/NewValuesEventArgs.h new file mode 100644 index 0000000000000000000000000000000000000000..cfe4f752030e6fd7b204e3c439dae2e5b69e70eb --- /dev/null +++ b/ModeliRpc/ModeliRpcCLR/NewValuesEventArgs.h @@ -0,0 +1,19 @@ +#pragma once +using namespace System; + +ref class NewValuesEventArgs: EventArgs +{ +public: + NewValuesEventArgs(); + property double Timestamp; + property String^ InstanceName; + property array<unsigned int>^ IntegerValueRefs; + property array<int>^ IntegerValues; + property array<unsigned int>^ RealValueRefs; + property array<double>^ RealValues; + property array<unsigned int>^ BoolValueRefs; + property array<bool>^ BoolValues; + property array<unsigned int>^ StringValueRefs; + property array<String^>^ StringValues; +}; + diff --git a/ModeliRpc/ModeliRpcNative/IRpcFrontend.h b/ModeliRpc/ModeliRpcNative/IRpcFrontend.h index df53ffd9d1e43f0e631035ac23e829bb0a7612f3..adf9824bd0c0fb5f1603c42ee1569b2479e8f785 100644 --- a/ModeliRpc/ModeliRpcNative/IRpcFrontend.h +++ b/ModeliRpc/ModeliRpcNative/IRpcFrontend.h @@ -2,16 +2,6 @@ #include <string> #include <vector> -enum fmi2Status -{ - fmi2OK, - fmi2Warning, - fmi2Discard, - fmi2Error, - fmi2Fatal, - fmi2Pending -}; - // Struct that will be used for the NewValues callback struct ValuesStruct { @@ -50,14 +40,17 @@ public: virtual void destroy() = 0; virtual void connect(std::string address, int port) = 0; virtual void registerCallbacks(NewValuesCallback newValuesCallback, LogCallback logCallback) = 0; - virtual fmi2Status play() = 0; + /// Returns fmi2Status + virtual int play() = 0; virtual void pause() = 0; - virtual fmi2Status stop() = 0; + /// Returns fmi2Status + virtual int stop() = 0; virtual bool addFmu(std::string instanceName, std::vector<unsigned char> fmuFile) = 0; virtual bool removeFmu(std::string instanceName) = 0; virtual bool addChannelLink(ChannelLink channelLink) = 0; virtual bool removeChannelink(ChannelLink channelLink) = 0; - virtual fmi2Status setValues(ValuesStruct values) = 0; + /// Returns fmi2Status + virtual int setValues(ValuesStruct values) = 0; }; // Factory signature extern "C" __declspec(dllexport) IRpcFrontend* createRpcFrontend(); \ No newline at end of file diff --git a/ModeliRpc/ModeliRpcNative/RpcFrontend.cpp b/ModeliRpc/ModeliRpcNative/RpcFrontend.cpp index 37397e6dddc790280fc2bcfe25545afec7c7dc72..0fc85464e72e107c6e383b20179c36435f9e96ac 100644 --- a/ModeliRpc/ModeliRpcNative/RpcFrontend.cpp +++ b/ModeliRpc/ModeliRpcNative/RpcFrontend.cpp @@ -36,14 +36,14 @@ void RpcFrontend::registerCallbacks(NewValuesCallback newValuesCallback, LogCall } -fmi2Status RpcFrontend::play() +int RpcFrontend::play() { if (!_client) { - return fmi2Error; + return FMI2_ERROR; } auto backend = _client->getMain<ModeliBackend>(); - return static_cast<fmi2Status>(backend.playRequest().send().wait(_client->getWaitScope()).getResult()); + return backend.playRequest().send().wait(_client->getWaitScope()).getResult(); } void RpcFrontend::pause() @@ -56,14 +56,14 @@ void RpcFrontend::pause() backend.pauseRequest().send().wait(_client->getWaitScope()); } -fmi2Status RpcFrontend::stop() +int RpcFrontend::stop() { if (!_client) { - return fmi2Error; + return FMI2_ERROR; } auto backend = _client->getMain<ModeliBackend>(); - return static_cast<fmi2Status>(backend.stopRequest().send().wait(_client->getWaitScope()).getResult()); + return backend.stopRequest().send().wait(_client->getWaitScope()).getResult(); } bool RpcFrontend::addFmu(std::string instanceName, std::vector<unsigned char> fmuFile) @@ -117,15 +117,15 @@ bool RpcFrontend::removeChannelink(ChannelLink channelLink) return request.send().wait(_client->getWaitScope()).getResult(); } -fmi2Status RpcFrontend::setValues(ValuesStruct values) +int RpcFrontend::setValues(ValuesStruct values) { if (!_client) { - return fmi2Error; + return FMI2_ERROR; } // Send request auto backend = _client->getMain<ModeliBackend>(); auto request = backend.setValuesRequest(); request.setValues(CapnConverter::convertValues(values)); - return static_cast<fmi2Status>(request.send().wait(_client->getWaitScope()).getResult()); + return request.send().wait(_client->getWaitScope()).getResult(); } \ No newline at end of file diff --git a/ModeliRpc/ModeliRpcNative/RpcFrontend.h b/ModeliRpc/ModeliRpcNative/RpcFrontend.h index e44ecd16f64d081b09ef1a6ab3f725c7296aec92..e4e1e54d395aa5d211adf5398f3a2c5c711a74e7 100644 --- a/ModeliRpc/ModeliRpcNative/RpcFrontend.h +++ b/ModeliRpc/ModeliRpcNative/RpcFrontend.h @@ -15,19 +15,20 @@ public: ~RpcFrontend(); private: std::unique_ptr<capnp::EzRpcClient> _client; + const int FMI2_ERROR = 3; // Inherited via IModeliFrontend void destroy() override; void connect(std::string address, int port) override; void registerCallbacks(NewValuesCallback newValuesCallback, LogCallback logCallback) override; - fmi2Status play() override; + int play() override; void pause() override; - fmi2Status stop() override; + int stop() override; bool addFmu(std::string instanceName, std::vector<unsigned char> fmuFile) override; bool removeFmu(std::string instanceName) override; bool addChannelLink(ChannelLink channelLink) override; bool removeChannelink(ChannelLink channelLink) override; - fmi2Status setValues(ValuesStruct values) override; + int setValues(ValuesStruct values) override; }; /// Factory method that returns an implementation of IRpcFrontend.