From 6bd91a85eb1b1139f351d7af1de9f1a64aad11be Mon Sep 17 00:00:00 2001 From: Hongwei Mao Date: Mon, 7 Jan 2019 17:06:44 -0500 Subject: [PATCH 1/4] Fixed Dragonfly.NET compilation error regarding the usage of array This compilation error was due to some changes made to the include structure in the C++ Standard Library headers since Visual Studio 2015, and the usage of "using namespace std;" in some header file that Dragonfly.NET depends on. The C++/CLI array in the cli namespace should be used, not the one in the std namespace. --- lang/dot_net/Dragonfly.NET.h | 2 +- lang/dot_net/Serializer.NET.h | 62 +++++++++++++++++------------------ 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/lang/dot_net/Dragonfly.NET.h b/lang/dot_net/Dragonfly.NET.h index 12544c2..ea78fba 100755 --- a/lang/dot_net/Dragonfly.NET.h +++ b/lang/dot_net/Dragonfly.NET.h @@ -180,7 +180,7 @@ namespace Dragonfly { mod->InitVariables( ModuleID, 0); // Convert server name string from .NET to regular C string int NumChars = ServerName->Length; - array ^charArray = ServerName->ToCharArray( ); + cli::array ^charArray = ServerName->ToCharArray( ); char server_name[1024]; if( NumChars > 1023) throw gcnew Exception( "ServerName argument is too long, 1023 is max"); for( int i = 0; i < NumChars; i++) { diff --git a/lang/dot_net/Serializer.NET.h b/lang/dot_net/Serializer.NET.h index f881233..9c8fbf0 100755 --- a/lang/dot_net/Serializer.NET.h +++ b/lang/dot_net/Serializer.NET.h @@ -77,7 +77,7 @@ namespace Dragonfly { TotalDataBytes = NumElements * ElementSize; } else { // If data is an array of objects, then get size of each element and add them up - array ^objArray = safe_cast^>( a); + cli::array ^objArray = safe_cast^>( a); for( i = 0; i < NumElements; i++) { Element = objArray[i]; NumDataBytes = CountDataBytes( Element); @@ -85,7 +85,7 @@ namespace Dragonfly { } } } else { // If data is an non-array object, then treat it as a struct and get the size of each field and add them up (non-data fields such as methods and pointers are ignored) - array ^fieldInfo = dataType->GetFields(); + cli::array ^fieldInfo = dataType->GetFields(); NumFields = fieldInfo->Length; for( i = 0; i < NumFields; i++) { Type ^fieldType = fieldInfo[i]->GetType(); @@ -197,7 +197,7 @@ namespace Dragonfly { case TypeCode::String: { String ^s = safe_cast( Data); NumElements = s->Length; - array ^charArray = s->ToCharArray( ); + cli::array ^charArray = s->ToCharArray( ); char *AsciiString = (char*) pOutput; for( int i = 0; i < NumElements; i++) { unsigned short UnicodeChar = charArray[i]; @@ -219,7 +219,7 @@ namespace Dragonfly { pEnd = SerializePrimitiveArray( Data, elementType, NumElements, pOutput); } else { // If data is an array of objects, then we copy each element by calling this function recursively - array ^objArray = safe_cast^>( a); + cli::array ^objArray = safe_cast^>( a); pEnd = pOutput; for( i = 0; i < NumElements; i++) { Object ^Element = objArray[i]; @@ -227,7 +227,7 @@ namespace Dragonfly { } } } else { // If data is an non-array object, then treat it as a struct and copy each field by calling this function recursively (non-data fields such as methods and pointers are ignored) - array ^fieldInfo = dataType->GetFields(); + cli::array ^fieldInfo = dataType->GetFields(); NumFields = fieldInfo->Length; pEnd = pOutput; for( i = 0; i < NumFields; i++) { @@ -285,18 +285,18 @@ namespace Dragonfly { int numBytes = numElements * elementSize; TypeCode ^typeCode = Type::GetTypeCode( primitiveType); switch( *typeCode) { - case TypeCode::Double: {array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} - case TypeCode::Single: {array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} - case TypeCode::SByte: {array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} - case TypeCode::Int16: {array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} - case TypeCode::Int32: {array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} - case TypeCode::Int64: {array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} - case TypeCode::Byte: {array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} - case TypeCode::UInt16: {array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} - case TypeCode::UInt32: {array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} - case TypeCode::UInt64: {array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} - case TypeCode::Boolean: {array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} - case TypeCode::Char: {array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} + case TypeCode::Double: {cli::array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} + case TypeCode::Single: {cli::array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} + case TypeCode::SByte: {cli::array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} + case TypeCode::Int16: {cli::array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} + case TypeCode::Int32: {cli::array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} + case TypeCode::Int64: {cli::array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} + case TypeCode::Byte: {cli::array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} + case TypeCode::UInt16: {cli::array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} + case TypeCode::UInt32: {cli::array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} + case TypeCode::UInt64: {cli::array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} + case TypeCode::Boolean: {cli::array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} + case TypeCode::Char: {cli::array ^source = safe_cast^>( Data); pin_ptr pSource = &source[0]; memcpy( pOutput, pSource, numBytes); break;} case TypeCode::String: throw gcnew Exception( "Attempting to access array of 'String' objects as primitive array"); case TypeCode::Object: throw gcnew Exception( "Attempting to access array of 'Object' type as primitive array"); default: throw gcnew Exception( "Unsupported data type"); @@ -349,7 +349,7 @@ namespace Dragonfly { pEnd = DeserializePrimitiveArray( Output, elementType, NumElements, pInput); } else { // If data is an array of objects, then we copy each element by calling this function recursively - array ^%objArray = safe_cast^%>( a); + cli::array ^%objArray = safe_cast^%>( a); pEnd = pInput; for( i = 0; i < NumElements; i++) { Object ^Element = objArray[i]; @@ -358,7 +358,7 @@ namespace Dragonfly { } } } else { // If data is an non-array object, then treat it as a struct and copy each field by calling this function recursively (non-data fields such as methods and pointers are ignored) - array ^fieldInfo = dataType->GetFields(); + cli::array ^fieldInfo = dataType->GetFields(); NumFields = fieldInfo->Length; pEnd = pInput; for( i = 0; i < NumFields; i++) { @@ -418,18 +418,18 @@ namespace Dragonfly { int numBytes = numElements * elementSize; TypeCode ^typeCode = Type::GetTypeCode( primitiveType); switch( *typeCode) { - case TypeCode::Double: {array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} - case TypeCode::Single: {array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} - case TypeCode::SByte: {array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} - case TypeCode::Int16: {array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} - case TypeCode::Int32: {array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} - case TypeCode::Int64: {array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} - case TypeCode::Byte: {array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} - case TypeCode::UInt16: {array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} - case TypeCode::UInt32: {array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} - case TypeCode::UInt64: {array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} - case TypeCode::Boolean: {array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} - case TypeCode::Char: {array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} + case TypeCode::Double: {cli::array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} + case TypeCode::Single: {cli::array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} + case TypeCode::SByte: {cli::array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} + case TypeCode::Int16: {cli::array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} + case TypeCode::Int32: {cli::array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} + case TypeCode::Int64: {cli::array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} + case TypeCode::Byte: {cli::array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} + case TypeCode::UInt16: {cli::array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} + case TypeCode::UInt32: {cli::array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} + case TypeCode::UInt64: {cli::array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} + case TypeCode::Boolean: {cli::array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} + case TypeCode::Char: {cli::array ^output = safe_cast^>( Output); pin_ptr pOutput = &output[0]; memcpy( pOutput, pInput, numBytes); break;} case TypeCode::String: throw gcnew Exception( "Attempting to access array of 'String' objects as primitive array"); case TypeCode::Object: throw gcnew Exception( "Attempting to access array of 'Object' type as primitive array"); default: throw gcnew Exception( "Unsupported data type"); From a33430200dc7c625cf3f12f42c6e143f2ceb7930 Mon Sep 17 00:00:00 2001 From: Hongwei Mao Date: Tue, 14 May 2019 19:36:59 -0400 Subject: [PATCH 2/4] Added support for Unity C# Wrapped the Dragonfly library into an unmanaged C++ DLL that can be loaded by Unity C# script. --- lang/unity/Dragonfly.Unity.Exception.h | 19 + lang/unity/Dragonfly.Unity.cpp | 3 + lang/unity/Dragonfly.Unity.h | 137 +++++ lang/unity/Dragonfly.Unity.sln | 31 ++ lang/unity/Dragonfly.Unity.vcxproj | 191 +++++++ lang/unity/Dragonfly.Unity.vcxproj.filters | 28 ++ lang/unity/TranslateDragonflyhFiles2Unity.m | 527 ++++++++++++++++++++ tools/build_unity_message_defs.m | 14 + 8 files changed, 950 insertions(+) create mode 100644 lang/unity/Dragonfly.Unity.Exception.h create mode 100644 lang/unity/Dragonfly.Unity.cpp create mode 100644 lang/unity/Dragonfly.Unity.h create mode 100644 lang/unity/Dragonfly.Unity.sln create mode 100644 lang/unity/Dragonfly.Unity.vcxproj create mode 100644 lang/unity/Dragonfly.Unity.vcxproj.filters create mode 100644 lang/unity/TranslateDragonflyhFiles2Unity.m create mode 100644 tools/build_unity_message_defs.m diff --git a/lang/unity/Dragonfly.Unity.Exception.h b/lang/unity/Dragonfly.Unity.Exception.h new file mode 100644 index 0000000..5284753 --- /dev/null +++ b/lang/unity/Dragonfly.Unity.Exception.h @@ -0,0 +1,19 @@ +#pragma once + +// shared exception +#define MOD_NOT_CONNECTED -100 + +// Native_ConnectToMMM() +#define MOD_CONNECTED -1 +#define MOD_CONNECT_FAILED -2 + +// Native_DisconnectFromMMM() +#define DISCONNECT_FAILED -1 + +// Native_ReadMessage() +#define READ_NO_MESSAGE -1 +#define READ_MALLOC_FAILED -3 + +// Native_SendMessage() +#define SEND_MESSAGE_FAILED 0 + diff --git a/lang/unity/Dragonfly.Unity.cpp b/lang/unity/Dragonfly.Unity.cpp new file mode 100644 index 0000000..55f9fc8 --- /dev/null +++ b/lang/unity/Dragonfly.Unity.cpp @@ -0,0 +1,3 @@ +// This is the main Unity DLL file. + +#include "Dragonfly.Unity.h" diff --git a/lang/unity/Dragonfly.Unity.h b/lang/unity/Dragonfly.Unity.h new file mode 100644 index 0000000..fca7618 --- /dev/null +++ b/lang/unity/Dragonfly.Unity.h @@ -0,0 +1,137 @@ +// Dragonfly.Unity.h +// +// Unity C# wrapper for the Dragonfly API +// +// Ref: +// https://www.mono-project.com/docs/advanced/pinvoke/ +// https://answers.unity.com/questions/1200157/nonstatic-extern-functions-from-dll-plugin-import.html +// +// Hongwei Mao 12/21/2018 +// +// Copyright (c) 2018 by Hongwei Mao, University of Pittsburgh. All rights reserved. + +#pragma once +#define DllExport __declspec( dllexport ) + +#include "Dragonfly.h" +#include "Dragonfly.Unity.Exception.h" + +//-------------------------------------------------------------------------------- +// The Module class +//-------------------------------------------------------------------------------- +extern "C" DllExport Dragonfly_Module* Native_Module() +{ + Dragonfly_Module* mod = new Dragonfly_Module(); + + return mod; +} + +extern "C" DllExport void Native_DestroyModule(Dragonfly_Module* mod) +{ + delete mod; +} + +// ConnectToMMM - Connect to the Message Manager Module, i.e. initiate communication with the Dragonfly system +// Throws exception if connection unsuccessful or if already connected +extern "C" DllExport int Native_ConnectToMMM(Dragonfly_Module* mod, MODULE_ID ModuleID, char* ServerName) +{ + if (mod->IsConnected()) + return MOD_CONNECTED; // already connected + + mod->InitVariables(ModuleID, (short)0); + + int res = mod->ConnectToMMM(ServerName, (int)0, (int)0, (int)0); + + if (res == 0) + return MOD_CONNECT_FAILED; + else + return 1; +} + +extern "C" DllExport int Native_DisconnectFromMMM(Dragonfly_Module* mod) +{ + if (mod->IsConnected()) + { + int res = mod->DisconnectFromMMM(); + + if (res == 0) + return DISCONNECT_FAILED; + else + return 1; + } + else + return MOD_NOT_CONNECTED; +} + +// int Dragonfly_Module::IsConnected(void) +extern "C" DllExport int Native_IsConnected(Dragonfly_Module* mod) +{ + return mod->IsConnected(); +} + +// int Dragonfly_Module::Subscribe(MSG_TYPE MessageType) +extern "C" DllExport int Native_Subscribe(Dragonfly_Module* mod, MSG_TYPE MessageType) +{ + if (!mod->IsConnected()) + return MOD_NOT_CONNECTED; + + return mod->Subscribe(MessageType); +} + +// Message ReadMessage(double timeout) in Dragonfly.NET.h +extern "C" DllExport int Native_ReadMessage(Dragonfly_Module* mod, double timeout, void*& pData, int& numBytes) +{ + pData = NULL; + + if (!mod->IsConnected()) + return MOD_NOT_CONNECTED; + + CMessage* msg = new CMessage(); + int msg_type = READ_NO_MESSAGE; + + int got_message = mod->ReadMessage(msg, timeout); + if (got_message) + { + // get the number of bytes of the message data + numBytes = msg->num_data_bytes; + + // For messages, numBytes > 0 + // For signals, numBytes = 0 + if (numBytes > 0) + { + // allocate unmanaged memory to copy message data over + pData = malloc(numBytes); + if (pData == NULL) + return READ_MALLOC_FAILED; + + msg->GetData(pData); + } + + msg_type = msg->msg_type; + } + + delete msg; + + return msg_type; +} + +extern "C" DllExport void Native_FreeMemory(void* pData) +{ + free(pData); +} + +extern "C" DllExport int Native_SendMessage(Dragonfly_Module* mod, MSG_TYPE MessageType, void* pMsg, int numBytes) +{ + if (!mod->IsConnected()) + return MOD_NOT_CONNECTED; + + CMessage M(MessageType); + M.Set(MessageType, pMsg, numBytes); + + // Send the message + if (!mod->SendMessageDF(&M)) + return SEND_MESSAGE_FAILED; + + return 1; +} + diff --git a/lang/unity/Dragonfly.Unity.sln b/lang/unity/Dragonfly.Unity.sln new file mode 100644 index 0000000..10f12f3 --- /dev/null +++ b/lang/unity/Dragonfly.Unity.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28306.52 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Dragonfly.Unity", "Dragonfly.Unity.vcxproj", "{48B7DF14-5703-4705-B4F8-44A888734B58}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {48B7DF14-5703-4705-B4F8-44A888734B58}.Debug|x64.ActiveCfg = Debug|x64 + {48B7DF14-5703-4705-B4F8-44A888734B58}.Debug|x64.Build.0 = Debug|x64 + {48B7DF14-5703-4705-B4F8-44A888734B58}.Debug|x86.ActiveCfg = Debug|Win32 + {48B7DF14-5703-4705-B4F8-44A888734B58}.Debug|x86.Build.0 = Debug|Win32 + {48B7DF14-5703-4705-B4F8-44A888734B58}.Release|x64.ActiveCfg = Release|x64 + {48B7DF14-5703-4705-B4F8-44A888734B58}.Release|x64.Build.0 = Release|x64 + {48B7DF14-5703-4705-B4F8-44A888734B58}.Release|x86.ActiveCfg = Release|Win32 + {48B7DF14-5703-4705-B4F8-44A888734B58}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8E570514-60F7-4EE2-9E5B-BE30D70AF3F8} + EndGlobalSection +EndGlobal diff --git a/lang/unity/Dragonfly.Unity.vcxproj b/lang/unity/Dragonfly.Unity.vcxproj new file mode 100644 index 0000000..c4aab1c --- /dev/null +++ b/lang/unity/Dragonfly.Unity.vcxproj @@ -0,0 +1,191 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + NotUsing + + + + + + + + + + + + + + 15.0 + {48B7DF14-5703-4705-B4F8-44A888734B58} + Win32Proj + DragonflyUnity + 10.0.16299.0 + + + + + + DynamicLibrary + true + v141 + Unicode + + + DynamicLibrary + false + v141 + true + Unicode + + + DynamicLibrary + true + v141 + Unicode + + + DynamicLibrary + false + v141 + true + Unicode + false + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + $(SolutionDir)\ + + + false + $(SolutionDir)\ + $(Configuration)\ + + + + Use + Level3 + Disabled + true + WIN32;_DEBUG;DRAGONFLYUNITY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + Use + Level3 + Disabled + true + _DEBUG;DRAGONFLYUNITY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;DRAGONFLYUNITY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + ..\..\include;..\..\include\internal; + + + Windows + true + true + true + Dragonfly.Unity.dll + ..\..\lib; + Dragonfly.lib + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;DRAGONFLYUNITY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + ..\..\include;..\..\include\internal; + + + + + Windows + true + true + true + Dragonfly.lib;%(AdditionalDependencies) + ..\..\lib; + Dragonfly.Unity.dll + + + + + + \ No newline at end of file diff --git a/lang/unity/Dragonfly.Unity.vcxproj.filters b/lang/unity/Dragonfly.Unity.vcxproj.filters new file mode 100644 index 0000000..7023826 --- /dev/null +++ b/lang/unity/Dragonfly.Unity.vcxproj.filters @@ -0,0 +1,28 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/lang/unity/TranslateDragonflyhFiles2Unity.m b/lang/unity/TranslateDragonflyhFiles2Unity.m new file mode 100644 index 0000000..e4b574e --- /dev/null +++ b/lang/unity/TranslateDragonflyhFiles2Unity.m @@ -0,0 +1,527 @@ +function TranslateDragonflyhFiles2Unity( Dragonfly_BaseDir, MessageDefFile) +% TranslateDragonflyhFiles2dotNET( Dragonfly_BaseDir, MessageDefFile) +% +% Translates C header file and associated +% h files to C# and other .NET languages. +% +% Dragonfly Base directory (Dragonfly_BaseDir) needs to be specified so that it can find the core +% definition files. +% +% MessageDefFile specifies the application-specific +% message definition file (e.g. '../../Source/Dragonfly_config.h'). + + this_file_path = mfilename( 'fullpath'); + this_dir_path = fileparts( this_file_path); + PATH = {[this_dir_path '/../matlab']}; + + addpath( PATH{:}); + + [ConfigFileDir, ConfigFileBaseName] = fileparts( MessageDefFile); + if(isempty(ConfigFileDir)) + ConfigFileDir='.'; + end + ConfigFile_BasePath = [ConfigFileDir '/' ConfigFileBaseName]; + Output_Namespace = 'Dragonfly'; + Translate( Dragonfly_BaseDir, ConfigFile_BasePath, Output_Namespace); + + rmpath( PATH{:}); + + +function Translate( Dragonfly_BaseDir, config_file_basename, output_namespace) + + global IndentLevel; + global IndentNumSpaces; + + IndentLevel = 0; + IndentNumSpaces = 4; + + DF = ReadDragonflyhFiles( Dragonfly_BaseDir, [config_file_basename '.h']); + + Languages = {'C#'}; + FileExtensions = {'_Unity.cs'}; + for i = 1 : length( Languages) + L = Languages{i}; + E = FileExtensions{i}; + f = fopen( [config_file_basename E], 'wt'); + WriteInclude(L, f); + BeginNamespace( output_namespace, L, f); + use_const = true; + force_int = true; + WriteClass( DF.MT, 'MT', L, f, use_const, force_int); + WriteClass( DF.MID, 'MID', L, f, use_const, force_int); + WriteClass( DF.HID, 'HID', L, f, use_const, force_int); + WriteClass( DF.MDF, 'MDF', L, f); + WriteClass( DF.MESSAGE_HEADER, 'MESSAGE_HEADER', L, f); + WriteClass( DF.defines, 'defines', L, f, use_const); + WriteClass( DF.typedefs, 'typedefs', L, f); + %DF.MTN_by_MT + %DF.MDF_by_MT + EndNamespace( L, f); + fclose( f); + end + + +% ReadDragonflyConfigFiles( Dragonfly_BaseDir, File1, File2, ...) +% +% Reads core message definition files from the Dragonfly base directory (Dragonfly_BaseDir) +% plus optionally any additional user specified files to get the host and module id-s, and +% message type id-s, and message header and data structures. If Dragonfly_BaseDir is empty, +% then only reads the specified files. +% +% Meel Velliste +% 4/19/2006 +% Amended 9/8/2008 to add flexibility to remove dependency on Dragonfly being installed +% in a fixed relative location to application modules. MV +function DF = ReadDragonflyhFiles( Dragonfly_BaseDir, varargin) + + % Process argument list + InputFilenames = {}; + for i = 1 : length( varargin) + arg = varargin{i}; + % Make sure arguments are strings + if( ~ischar(arg)), error( 'ReadDragonflyhFiles() only takes string arguments'); end + InputFilenames(end+1) = {arg}; + end + + FilePaths = {}; + + % If Dragonfly base directory provided, then add core definition file to the list + if( ~isempty( Dragonfly_BaseDir)) + FilePaths(end+1) = {[Dragonfly_BaseDir '/include/Dragonfly_types.h']}; + end + + % Add user defined files to the list + FilePaths = [FilePaths InputFilenames]; + + % Parse the file list + h = ParseHFile( FilePaths{:}); + + DF.HID = []; % Host ID-s + DF.MID = []; % Module ID-s + DF.MT = []; % Message Types + DF.MDF = []; % Message Data Formats + + % Go through the defines and find the host ID-s (HID), module ID-s (MID) + % and message types (MT), then find the messgae data formats (MDF) in + % the tyepdefs + DF = ExtractFields( h.defines, DF); + DF = ExtractFields( h.typedefs, DF); + + % Check that all HID, MID and MT values are numeric + CheckNumericIDs( DF); + + % Check for duplicate message types + CheckDupes( DF.MT); + + % Message header template for the ReadMessage and SendMessage functions + DF.MESSAGE_HEADER = h.typedefs.DF_MSG_HEADER; + + % Create a lookup table to get Message Type Name by Message Type ID + DF.MTN_by_MT = {}; + message_names = fieldnames( DF.MT); + for i = 1 : length( message_names) + message_name = message_names{i}; + message_type_id = getfield( DF.MT, message_name); + DF.MTN_by_MT{message_type_id+1,1} = message_name; + end + + % Create a lookup table to get Message Data Format by Message Type ID + DF.MDF_by_MT = cell( size( DF.MTN_by_MT)); + message_names = fieldnames( DF.MDF); + for i = 1 : length( message_names) + message_name = message_names{i}; + % Make sure there is a matching MT field + if( ~isfield( DF.MT, message_name)) + error( ['MDF_' message_name ' does not have a matching MT_' message_name ' defined']); + end + message_type_id = getfield( DF.MT, message_name); + DF.MDF_by_MT{message_type_id+1,1} = getfield( DF.MDF, message_name); + end + + % If Dragonfly base directory provided, then parse mex op-code definition file + % to get the op-codes for calling the MatlabDragonfly mex file + if( ~isempty( Dragonfly_BaseDir)) + MatlabDragonfly_h = ParseHFile( [Dragonfly_BaseDir '/lang/matlab/MatlabDragonfly.h']); + DF.mex_opcode = MatlabDragonfly_h.defines; + end + + % Put the raw parsed h-file content in the Dragonfly struct as well in case + % we want to access some of the non-Dragonfly-specific defines + DF.defines = h.defines; + DF.typedefs = h.typedefs; + DF.vars = h.vars; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function DF = ExtractFields( fields, DF) + + names = fieldnames( fields); + for n = 1 : length( names) + name = names{n}; + [prefix, remainder] = strtok( name, '_'); + realname = remainder( 2:end); + value = getfield( fields, name); + + % Check that HID, MID and MT fields are integer values + switch( prefix) + case {'HID','MID','MT'} + if( value ~= int32( value)) + error('HID_, MID_ and MT_ values should be integers'); + end + end + + % Put the fields and their values in the appropriate fields of the Dragonfly + % structure + switch( prefix) + case 'HID', DF.HID = setfield( DF.HID, realname, value); + case 'MID', DF.MID = setfield( DF.MID, realname, value); + case 'MT', DF.MT = setfield( DF.MT, realname, value); + case 'MDF', DF.MDF = setfield( DF.MDF, realname, value); + end + end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function CheckNumericIDs( Struct) + + id_types = {'HID', 'MID', 'MT'}; + + for t = 1 : length( id_types) + id_type = id_types{t}; + Names = fieldnames( Struct.(id_type)); + Values = struct2cell( Struct.(id_type)); + for i = 1 : length( Values) + value = Values{i}; + switch( class( value)) + case 'double' + otherwise + error( ['Value for ' id_type '_' Names{i} ' is not numeric: "' value '"']); + end + end + end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function CheckDupes( Struct) + + Names = fieldnames( Struct); + ValuesCell = struct2cell( Struct); + Values = [ValuesCell{:}]; + UniqueValues = unique( Values); + NumValues = length( Values); + NumUnique = length( UniqueValues); + if( NumValues ~= NumUnique) + sortedValues = sort(Values); + idx = find(diff(sortedValues) == 0); + error([ 'Duplicate message type ID-s found: ' num2str(sortedValues(idx)) ]); + end + + +function WriteInclude( language, f) + global Indent; + if ( strcmp( language, 'C#')) + fprintf( f, [Indent 'using System.Runtime.InteropServices;\n' Indent '\n']); + end + +function BeginNamespace( name, language, f) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% BeginNamespace( name, language, f) - write a begin namespace +% + global Indent; + switch( language) + case 'C++' + fprintf( f, [Indent 'namespace ' name ' {\n' ... + Indent '\n']); + case 'C#' + fprintf( f, [Indent 'namespace ' name '\n' ... + Indent '{\n']); + case 'VB', error( 'Sorry, VB output not implemented yet'); + otherwise, error( 'Unrecognized language'); + end + IncreaseIndent( ); + + +function EndNamespace( language, f) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% EndNamespace( language, f) - write an end namespace +% + global Indent; + DecreaseIndent( ); + switch( language) + case 'C++', fprintf( f, [Indent '}\n\n']); + case 'C#', fprintf( f, [Indent '}\n\n']); + case 'VB', error( 'Sorry, VB output not implemented yet'); + otherwise, error( 'Unrecognized language'); + end + + +function WriteEnum( enum_definitions, enum_name, language, f) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% WriteEnum( enum_definitions, enum_name, language, f) - Write an enumeration definition +% +% enum_definitions - a struct where field names are enum names and values are enum values +% enum_name - name of the enum in the output file +% language - a string that defines output language ("C++", "C#" or "VB") +% f - file handle of output file + + global Indent; + + % Begin the enum definition + switch( language) + case 'C++' + fprintf( f, [Indent 'public enum class' enum_name '\n' ... + Indent '{\n']); + case 'C#' + fprintf( f, [Indent 'public enum ' enum_name '\n' ... + Indent '{\n']); + case 'VB', error( 'Sorry, VB output not implemented yet'); + otherwise, error( 'Unrecognized language'); + end + + % Write the defined values + IncreaseIndent( ); + names = fieldnames( enum_definitions); + for i = 1 : length( names) + name = names{i}; + value = enum_definitions.(name); + if( value ~= int32(value)), error( 'Value not suitable for "enum"'), end + if( i == length( names)), comma = ''; else comma = ','; end + switch( language) + case 'C++', fprintf( f, [Indent name ' = ' num2str(value) comma '\n']); + case 'C#', fprintf( f, [Indent name ' = ' num2str(value) comma '\n']); + end + end + DecreaseIndent( ); + + % End the enum definition + switch( language) + case 'C++', fprintf( f, [Indent '};\n\n']); + case 'C#', fprintf( f, [Indent '}\n\n']); + end + + +function WriteClass( member_definitions, class_name, language, f, use_const, force_int) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% WriteClass( member_definitions, class_name, language, f, const_int) - Write a class definition +% +% member_definitions - a struct whose field names are class member names and values serve as a template for the type definition of the member +% class_name - name of the class in the output file +% language - a string that defines output language ("C++", "C#" or "VB") +% f - file handle of output file +% use_const_int - optional argument. If present, and if "true", then all members will have type "const int" and the value will be written + + global Indent; + global IndentLevel; + + if( ~exist( 'use_const', 'var')) + use_const = false; + end + if( ~exist( 'force_int', 'var')) + force_int = false; + end + + % Begin the class definition + switch( language) + case 'C++' + fprintf( f, [Indent 'ref class ' class_name '\n' ... + Indent '{\n' ... + Indent 'public:\n']); + case 'C#' + fprintf(f, [Indent '[StructLayout(LayoutKind.Sequential)]\n']); + fprintf( f, [Indent 'public class ' class_name '\n' ... + Indent '{\n']); + case 'VB', error( 'Sorry, VB output not implemented yet'); + otherwise, error( 'Unrecognized language'); + end + + % Write the defined values + IncreaseIndent( ); + names = fieldnames( member_definitions); + for i = 1 : length( names) + name = names{i}; + value = member_definitions.(name); + if( use_const) + if( force_int) + if( value ~= int32(value)), error( 'Value not suitable for "const int"'), end + type = 'int'; + else + if( IsInteger( value)) + type = 'int'; + else + type = 'double'; + end + end + if( ischar( value)) + value(value=='"') = []; % Eliminate quote characters because they will not work inside C++ or C# source code + switch( language) + case 'C++', fprintf( f, [Indent 'static const char ' name '[] = "' value '";\n']); + case 'C#', fprintf( f, [Indent 'public const string ' name ' = "' value '";\n']); + end + else + switch( language) + case 'C++', fprintf( f, [Indent 'static const ' type ' ' name ' = ' num2str(value) ';\n']); + case 'C#', fprintf( f, [Indent 'public const ' type ' ' name ' = ' num2str(value) ';\n']); + end + end + else + switch( language) + case 'C++', WriteMemberCPP( name, value, f); + case 'C#', WriteMemberCS( name, value, f); + end + end + end + DecreaseIndent( ); + + % End the class definition + if( IndentLevel <= 2), extra_newline = '\n'; else extra_newline = ''; end + switch( language) + case 'C++', fprintf( f, [Indent '};\n' extra_newline]); + case 'C#', fprintf( f, [Indent '}\n' extra_newline]); + end + + +function WriteMemberCS( name, value, f) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% WriteMemberCS( name, value, f) - Write class member in C# notation +% +% name - Member name +% value - the type of the value is used to infer the .NET type + + global Indent; + global IndentLevel; + + matlab_type = class( value); + + switch( matlab_type) + case 'char' + if( ~isempty(strfind( value, 'VARIABLE_LENGTH_ARRAY('))) + matlab_type = value(23:end-1); + value = []; + else + error( '"char" type not implemented'); + end + end + + switch( matlab_type) + case 'int8', type = 'sbyte'; + case 'int16', type = 'short'; + case 'int32', type = 'int'; + case 'int64', type = 'long'; + case 'uint8', type = 'byte'; + case 'uint16', type = 'ushort'; + case 'uint32', type = 'uint'; + case 'uint64', type = 'ulong'; + case 'double', type = 'double'; + case 'single', type = 'float'; + case 'struct' + if( IndentLevel > 2) + type = [name '_type']; + WriteClass( value, type, 'C#', f); + fprintf( f, [Indent 'public ' type ' ' name ' = new ' type '();\n\n']); + else + WriteClass( value, name, 'C#', f); + end + return + otherwise, error( 'Unsupported type'); + end + + if( isempty( value)) % If value is empty, then it designates a variable length array + fprintf( f, [Indent 'public ' type '[] ' name ';\n']); + else + + if( ~isvector( value)), error( 'Matrices are not yet supported, value must be scalar or vector'); end + vector_length = length( value); + if( vector_length > 1) + initializing_zeros = repmat('0,',[1 vector_length]); + initializing_zeros(end) = []; % Eliminate last comma + fprintf( f, [Indent '[MarshalAs(UnmanagedType.ByValArray, SizeConst = ' ... + num2str(vector_length) ')]\n']); + fprintf( f, [Indent 'public ' type '[] ' name ' = {' initializing_zeros '};\n']); + else + fprintf( f, [Indent 'public ' type ' ' name ';\n']); + end + end + + +function WriteMemberCPP( name, value, f) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% WriteMemberCPP( name, value, f) - Write class member in C++ notation +% +% name - Member name +% value - the type of the value is used to infer the .NET type + + global Indent; + + matlab_type = class( value); + + switch( matlab_type) + case 'char' + if( ~isempty(strfind( value, 'VARIABLE_LENGTH_ARRAY('))) + matlab_type = value(23:end-1); + value = []; + else + error( '"char" type not implemented'); + end + end + + switch( matlab_type) + case 'int8', type = 'System::SByte'; + case 'int16', type = 'System::Int16'; + case 'int32', type = 'System::Int32'; + case 'int64', type = 'System::Int64'; + case 'uint8', type = 'System::Byte'; + case 'uint16', type = 'System::UInt16'; + case 'uint32', type = 'System::UInt32'; + case 'uint64', type = 'System::UInt64'; + case 'double', type = 'System::Double'; + case 'single', type = 'System::Single'; + case 'struct', WriteClass( value, name, 'C++', f); return + otherwise, error( 'Unsupported type'); + end + + if( isempty( value)) % If value is empty, then it designates a variable length array + fprintf( f, [Indent 'static array<' type '> ^' name ';\n']); + else + + if( ~isvector( value)), error( 'Matrices are not yet supported, value must be scalar or vector'); end + vector_length = length( value); + if( vector_length > 1) + initializing_zeros = repmat('0,',[1 vector_length]); + initializing_zeros(end) = []; % Eliminate last comma + fprintf( f, [Indent 'static array<' type '> ^' name ' = {' initializing_zeros '};\n']); + else + fprintf( f, [Indent 'static ' type ' ' name ';\n']); + end + end + + +function IncreaseIndent( ) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +global IndentLevel; +global IndentNumSpaces; +global Indent; + +IndentLevel = IndentLevel + 1; +if( IndentLevel > 10), error( 'IndentLevel > 10'); end +Indent = repmat( ' ', [1 IndentLevel*IndentNumSpaces]); + + +function DecreaseIndent( ) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +global IndentLevel; +global IndentNumSpaces; +global Indent; + +IndentLevel = IndentLevel - 1; +if( IndentLevel < 0), error( 'IndentLevel < 0'); end +Indent = repmat( ' ', [1 IndentLevel*IndentNumSpaces]); + + +function is_integer = IsInteger( value) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + if( value == int32(value)) + is_integer = true; + else + is_integer = false; + end + \ No newline at end of file diff --git a/tools/build_unity_message_defs.m b/tools/build_unity_message_defs.m new file mode 100644 index 0000000..849ded8 --- /dev/null +++ b/tools/build_unity_message_defs.m @@ -0,0 +1,14 @@ +function build_unity_message_defs(header_filename) + +% build_unity_message_defs(header_filename) +% +% header_filename is the name of the C header file to be translated and +% includes full path address. +% +% Meel Velliste 8/10/2008 +% Emrah Diril 9/10/2011 +% Hongwei Mao 1/4/2019 + + DragonflyPath = getenv('DRAGONFLY'); + addpath( [DragonflyPath '/lang/unity/']); + TranslateDragonflyhFiles2Unity(DragonflyPath, header_filename) From 5c5fb87668193e9744e8d2832087f15067d2ce46 Mon Sep 17 00:00:00 2001 From: Hongwei Mao Date: Fri, 1 Nov 2019 13:54:48 -0400 Subject: [PATCH 3/4] Update LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 748d996..2fbea86 100755 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ Dragonfly is licensed under the terms of the Revised BSD License as follows: -Copyright 2006-2013, University of Pittsburgh +Copyright 2006-2019, University of Pittsburgh All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following From 8028ca28fc42097b15402414c3cab2b74daf0873 Mon Sep 17 00:00:00 2001 From: Emrah Diril Date: Thu, 13 Feb 2020 00:01:36 -0500 Subject: [PATCH 4/4] Update year to 2020 --- LICENSE | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 2fbea86..89be125 100755 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ Dragonfly is licensed under the terms of the Revised BSD License as follows: -Copyright 2006-2019, University of Pittsburgh +Copyright 2006-2020, University of Pittsburgh All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following @@ -19,4 +19,4 @@ BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.