From 54fa695b6ad96a95f6ade87aa6a750cd543d0a07 Mon Sep 17 00:00:00 2001 From: jose_morato Date: Sat, 12 Oct 2024 08:53:34 +0200 Subject: [PATCH 1/2] [feature] Typed DataWriter full CDR serialization --- Native/CSharpCDRImplTemplate.txt | 188 ++- Native/CSharpJsonImplTemplate.txt | 10 + Native/CWrapperHeaderTemplate.txt | 16 + Native/CWrapperImplTemplate.txt | 64 + Sources/OpenDDSharp/DDS/DataWriter.cs | 6 +- Sources/OpenDDSharp/DDS/ITypeSupport.cs | 14 + .../OpenDDSharp.UnitTest/DataWriterCDRTest.cs | 1294 +++++++++++++++++ Tests/OpenDDSharp.UnitTest/DataWriterTest.cs | 2 +- .../TransportRegistryTest.cs | 12 +- Tests/TestIdlCdr/TestIdlCdr.csproj | 1 + 10 files changed, 1500 insertions(+), 107 deletions(-) create mode 100644 Tests/OpenDDSharp.UnitTest/DataWriterCDRTest.cs diff --git a/Native/CSharpCDRImplTemplate.txt b/Native/CSharpCDRImplTemplate.txt index 5aeac32e..6e8f8fca 100644 --- a/Native/CSharpCDRImplTemplate.txt +++ b/Native/CSharpCDRImplTemplate.txt @@ -1,6 +1,23 @@ public class <%TYPE%>TypeSupport : ITypeSupport<<%TYPE%>> { #region Fields + private static readonly JsonSerializerOptions _serializerOptions = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + WriteIndented = false, + AllowTrailingCommas = true, + Converters = + { + new JsonStringEnumConverter(), + new OctetArrayConverter(), + new FloatJsonConverter(), + new DoubleJsonConverter(), + new DecimalJsonConverter(), + }, + }; + + private static readonly <%TYPE%>SerializerContext _serializerContext = new <%TYPE%>SerializerContext(_serializerOptions); + private IntPtr _native; #endregion @@ -29,14 +46,12 @@ public string EncodeToString(<%TYPE%> sample) { - return string.Empty; // sample.ToCDR(); + return JsonSerializer.Serialize(sample, typeof(<%TYPE%>), _serializerContext); } - public <%TYPE%> DecodeFromString(string data) + public <%TYPE%> DecodeFromString(string str) { - var sample = new <%TYPE%>(); - //sample.FromCDR(data); - return sample; + return JsonSerializer.Deserialize(str, typeof(<%TYPE%>), _serializerContext) as <%TYPE%>; } public byte[] EncodeToBytes(<%TYPE%> sample) @@ -59,22 +74,22 @@ #if NET7_0_OR_GREATER [SuppressUnmanagedCodeSecurity] [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>TypeSupport_new", StringMarshalling = StringMarshalling.Utf8)] - [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvSuppressGCTransition) })] internal static partial IntPtr <%TYPE%>TypeSupportNew(); [SuppressUnmanagedCodeSecurity] [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>TypeSupport_GetTypeName", StringMarshalling = StringMarshalling.Utf8)] - [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvSuppressGCTransition) })] internal static partial IntPtr GetTypeName(IntPtr native); [SuppressUnmanagedCodeSecurity] [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>TypeSupport_RegisterType", StringMarshalling = StringMarshalling.Utf8)] - [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvSuppressGCTransition) })] internal static partial int RegisterType(IntPtr native, IntPtr dp, string typeName); [SuppressUnmanagedCodeSecurity] [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>TypeSupport_UnregisterType", StringMarshalling = StringMarshalling.Utf8)] - [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvSuppressGCTransition) })] internal static partial int UnregisterType(IntPtr native, IntPtr dp, string typeName); #else [SuppressUnmanagedCodeSecurity] @@ -95,6 +110,12 @@ #endif } + [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Default)] + [JsonSerializable(typeof(<%TYPE%>))] + public partial class <%TYPE%>SerializerContext : JsonSerializerContext + { + } + public class <%TYPE%>DataWriter : DataWriter { #region Fields @@ -128,10 +149,9 @@ { InstanceHandle ret = InstanceHandle.HandleNil; - var str = _typeSupport.EncodeToString(instance); - var json_data = MarshalHelper.NativeUtf8FromString(str); + var bytes = _typeSupport.EncodeToBytes(instance); - ret = <%TYPE%>DataWriterNative.RegisterInstanceTimestamp(_native, json_data, timestamp); + ret = <%TYPE%>DataWriterNative.RegisterInstanceTimestamp(_native, bytes, (UIntPtr)bytes.Length, timestamp); return ret; } @@ -144,26 +164,23 @@ return ReturnCode.PreconditionNotMet; } - var str = _typeSupport.EncodeToString(data); - var json_data = MarshalHelper.NativeUtf8FromString(str); + var bytes = _typeSupport.EncodeToBytes(data); - return (ReturnCode)<%TYPE%>DataWriterNative.UnregisterInstance(_native, json_data, handle); + return (ReturnCode)<%TYPE%>DataWriterNative.UnregisterInstance(_native, bytes, (UIntPtr)bytes.Length, handle); } public ReturnCode UnregisterInstance(<%TYPE%> data, InstanceHandle handle) { - var str = _typeSupport.EncodeToString(data); - var json_data = MarshalHelper.NativeUtf8FromString(str); + var bytes = _typeSupport.EncodeToBytes(data); - return (ReturnCode)<%TYPE%>DataWriterNative.UnregisterInstance(_native, json_data, handle); + return (ReturnCode)<%TYPE%>DataWriterNative.UnregisterInstance(_native, bytes, (UIntPtr)bytes.Length, handle); } public ReturnCode UnregisterInstance(<%TYPE%> data, InstanceHandle handle, Timestamp timestamp) { - var str = _typeSupport.EncodeToString(data); - var json_data = MarshalHelper.NativeUtf8FromString(str); + var bytes = _typeSupport.EncodeToBytes(data); - return (ReturnCode)<%TYPE%>DataWriterNative.UnregisterInstanceTimestamp(_native, json_data, handle, timestamp); + return (ReturnCode)<%TYPE%>DataWriterNative.UnregisterInstanceTimestamp(_native, bytes, (UIntPtr)bytes.Length, handle, timestamp); } public ReturnCode Write(<%TYPE%> data) @@ -196,10 +213,9 @@ ReturnCode ret = ReturnCode.Error; - var str = _typeSupport.EncodeToString(data); - var json_data = MarshalHelper.NativeUtf8FromString(str); + var bytes = _typeSupport.EncodeToBytes(data); - ret = (ReturnCode)<%TYPE%>DataWriterNative.WriteWithTimestamp(_native, json_data, handle, timestamp); + ret = (ReturnCode)<%TYPE%>DataWriterNative.WriteWithTimestamp(_native, bytes, (UIntPtr)bytes.Length, handle, timestamp); return ret; } @@ -218,10 +234,9 @@ ReturnCode ret = ReturnCode.Error; - var str = _typeSupport.EncodeToString(data); - var json_data = MarshalHelper.NativeUtf8FromString(str); + var bytes = _typeSupport.EncodeToBytes(data); - ret = (ReturnCode)<%TYPE%>DataWriterNative.Dispose(_native, json_data, handle); + ret = (ReturnCode)<%TYPE%>DataWriterNative.Dispose(_native, bytes, (UIntPtr)bytes.Length, handle); return ret; } @@ -235,10 +250,9 @@ ReturnCode ret = ReturnCode.Error; - var str = _typeSupport.EncodeToString(data); - var json_data = MarshalHelper.NativeUtf8FromString(str); + var bytes = _typeSupport.EncodeToBytes(data); - ret = (ReturnCode)<%TYPE%>DataWriterNative.DisposeTimestamp(_native, json_data, handle, timestamp); + ret = (ReturnCode)<%TYPE%>DataWriterNative.DisposeTimestamp(_native, bytes, (UIntPtr)bytes.Length, handle, timestamp); return ret; } @@ -257,22 +271,18 @@ ReturnCode ret = ReturnCode.Error; - var str = _typeSupport.EncodeToString(data); - var ptr = MarshalHelper.NativeUtf8FromString(str); - ret = (ReturnCode)<%TYPE%>DataWriterNative.GetKeyValue(_native, ref ptr, handle); + var size = UIntPtr.Zero; + var ptr = IntPtr.Zero; + + ret = (ReturnCode)<%TYPE%>DataWriterNative.GetKeyValue(_native, ref ptr, ref size, handle); if (ret == ReturnCode.Ok) { - var json_data = MarshalHelper.StringFromNativeUtf8(ptr) ?? string.Empty; - - if (!string.IsNullOrWhiteSpace(json_data)) - { - var sample = _typeSupport.DecodeFromString(json_data); - - data.MemberwiseCopy(sample); - } + byte[] managedArray = new byte[(int)size]; + Marshal.Copy(ptr, managedArray, 0, (int)size); - MarshalHelper.ReleaseNativeStringPointer(ptr); + var sample = _typeSupport.DecodeFromBytes(managedArray); + data.MemberwiseCopy(sample); } return ret; @@ -282,10 +292,9 @@ { InstanceHandle ret = InstanceHandle.HandleNil; - var str = _typeSupport.EncodeToString(instance); - var json_data = MarshalHelper.NativeUtf8FromString(str); + var bytes = _typeSupport.EncodeToBytes(instance); - ret = <%TYPE%>DataWriterNative.LookupInstance(_native, json_data); + ret = <%TYPE%>DataWriterNative.LookupInstance(_native, bytes, (UIntPtr)bytes.Length); return ret; } @@ -297,65 +306,54 @@ #if NET7_0_OR_GREATER [SuppressUnmanagedCodeSecurity] [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_Narrow")] - [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvSuppressGCTransition) })] internal static partial IntPtr Narrow(IntPtr dw); [SuppressUnmanagedCodeSecurity] - [SuppressGCTransition] [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_Write_Cdr")] [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvSuppressGCTransition) })] internal static partial int Write(IntPtr dw, byte[] cdrData, UIntPtr size, int handle); [SuppressUnmanagedCodeSecurity] - [SuppressUnmanagedCodeSecurity] - [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_WriteWithTimestamp_Json", CallingConvention = CallingConvention.Cdecl)] - internal static extern int WriteWithTimestamp(IntPtr dw, [In] IntPtr jsonData, int handle, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); + [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_WriteWithTimestamp_Cdr", CallingConvention = CallingConvention.Cdecl)] + internal static extern int WriteWithTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, int handle, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); [SuppressUnmanagedCodeSecurity] - [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_RegisterInstance_Json", StringMarshalling = StringMarshalling.Utf8)] - [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - internal static partial int RegisterInstance(IntPtr dw, IntPtr jsonData); - - [SuppressUnmanagedCodeSecurity] - [SuppressGCTransition] [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_RegisterInstance_Cdr")] [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvSuppressGCTransition) })] internal static partial int RegisterInstance(IntPtr dw, byte[] cdrData, UIntPtr size); [SuppressUnmanagedCodeSecurity] - [SuppressUnmanagedCodeSecurity] - [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_RegisterInstanceTimestamp_Json", CallingConvention = CallingConvention.Cdecl)] - internal static extern int RegisterInstanceTimestamp(IntPtr dw, [In] IntPtr jsonData, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); + [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_RegisterInstanceTimestamp_Cdr", CallingConvention = CallingConvention.Cdecl)] + internal static extern int RegisterInstanceTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); [SuppressUnmanagedCodeSecurity] - [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_UnregisterInstance_Json", StringMarshalling = StringMarshalling.Utf8)] - [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - internal static partial int UnregisterInstance(IntPtr dw, IntPtr jsonData, int handle); + [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_UnregisterInstance_Cdr")] + [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvSuppressGCTransition) })] + internal static partial int UnregisterInstance(IntPtr dw, byte[] cdrData, UIntPtr size, int handle); [SuppressUnmanagedCodeSecurity] - [SuppressUnmanagedCodeSecurity] - [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_UnregisterInstanceTimestamp_Json", CallingConvention = CallingConvention.Cdecl)] - internal static extern int UnregisterInstanceTimestamp(IntPtr dw, [In] IntPtr jsonData, int handle, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); + [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_UnregisterInstanceTimestamp_Cdr", CallingConvention = CallingConvention.Cdecl)] + internal static extern int UnregisterInstanceTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, int handle, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); [SuppressUnmanagedCodeSecurity] - [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_LookupInstance_Json", StringMarshalling = StringMarshalling.Utf8)] - [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - internal static partial int LookupInstance(IntPtr dw, IntPtr jsonData); + [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_LookupInstance_Cdr")] + [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvSuppressGCTransition) })] + internal static partial int LookupInstance(IntPtr dw, byte[] cdrData, UIntPtr size); [SuppressUnmanagedCodeSecurity] - [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_Dispose_Json", StringMarshalling = StringMarshalling.Utf8)] - [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - internal static partial int Dispose(IntPtr dw, IntPtr jsonData, int handle); + [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_Dispose_Cdr")] + [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvSuppressGCTransition) })] + internal static partial int Dispose(IntPtr dw, byte[] cdrData, UIntPtr size, int handle); [SuppressUnmanagedCodeSecurity] - [SuppressUnmanagedCodeSecurity] - [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_DisposeTimestamp_Json", CallingConvention = CallingConvention.Cdecl)] - internal static extern int DisposeTimestamp(IntPtr dw, [In] IntPtr jsonData, int handle, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); + [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_DisposeTimestamp_Cdr", CallingConvention = CallingConvention.Cdecl)] + internal static extern int DisposeTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, int handle, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); [SuppressUnmanagedCodeSecurity] - [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_GetKeyValue_Json", StringMarshalling = StringMarshalling.Utf8)] - [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - internal static partial int GetKeyValue(IntPtr dw, ref IntPtr json_data, int handle); + [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_GetKeyValue_Cdr")] + [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvSuppressGCTransition) })] + internal static partial int GetKeyValue(IntPtr dw, ref IntPtr cdrData, ref UIntPtr size, int handle); #else [SuppressUnmanagedCodeSecurity] [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_Narrow", CallingConvention = CallingConvention.Cdecl)] @@ -366,44 +364,40 @@ internal static extern int Write(IntPtr dw, byte[] cdrData, UIntPtr size, int handle); [SuppressUnmanagedCodeSecurity] - [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_WriteWithTimestamp_Json", CallingConvention = CallingConvention.Cdecl)] - internal static extern int WriteWithTimestamp(IntPtr dw, [In] IntPtr jsonData, int handle, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); - - [SuppressUnmanagedCodeSecurity] - [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_RegisterInstance_Json", CallingConvention = CallingConvention.Cdecl)] - internal static extern int RegisterInstance(IntPtr dw, [In] IntPtr jsonData); + [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_WriteWithTimestamp_Cdr", CallingConvention = CallingConvention.Cdecl)] + internal static extern int WriteWithTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, int handle, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); [SuppressUnmanagedCodeSecurity] [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_RegisterInstance_Cdr", CallingConvention = CallingConvention.Cdecl)] internal static extern int RegisterInstance(IntPtr dw, byte[] cdrData, UIntPtr size); [SuppressUnmanagedCodeSecurity] - [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_RegisterInstanceTimestamp_Json", CallingConvention = CallingConvention.Cdecl)] - internal static extern int RegisterInstanceTimestamp(IntPtr dw, [In] IntPtr jsonData, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); + [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_RegisterInstanceTimestamp_Cdr", CallingConvention = CallingConvention.Cdecl)] + internal static extern int RegisterInstanceTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); [SuppressUnmanagedCodeSecurity] - [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_UnregisterInstance_Json", CallingConvention = CallingConvention.Cdecl)] - internal static extern int UnregisterInstance(IntPtr dw, [In] IntPtr jsonData, int handle); + [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_UnregisterInstance_Cdr", CallingConvention = CallingConvention.Cdecl)] + internal static extern int UnregisterInstance(IntPtr dw, byte[] cdrData, UIntPtr size, int handle); [SuppressUnmanagedCodeSecurity] - [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_UnregisterInstanceTimestamp_Json", CallingConvention = CallingConvention.Cdecl)] - internal static extern int UnregisterInstanceTimestamp(IntPtr dw, [In] IntPtr jsonData, int handle, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); + [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_UnregisterInstanceTimestamp_Cdr", CallingConvention = CallingConvention.Cdecl)] + internal static extern int UnregisterInstanceTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, int handle, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); [SuppressUnmanagedCodeSecurity] - [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_LookupInstance_Json", CallingConvention = CallingConvention.Cdecl)] - internal static extern int LookupInstance(IntPtr dw, [In] IntPtr jsonData); + [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_LookupInstance_Cdr", CallingConvention = CallingConvention.Cdecl)] + internal static extern int LookupInstance(IntPtr dw, byte[] cdrData, UIntPtr size); [SuppressUnmanagedCodeSecurity] - [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_Dispose_Json", CallingConvention = CallingConvention.Cdecl)] - internal static extern int Dispose(IntPtr dw, [In] IntPtr jsonData, int handle); + [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_Dispose_Cdr", CallingConvention = CallingConvention.Cdecl)] + internal static extern int Dispose(IntPtr dw, byte[] cdrData, UIntPtr size, int handle); [SuppressUnmanagedCodeSecurity] - [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_DisposeTimestamp_Json", CallingConvention = CallingConvention.Cdecl)] - internal static extern int DisposeTimestamp(IntPtr dw, [In] IntPtr jsonData, int handle, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); + [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_DisposeTimestamp_Cdr", CallingConvention = CallingConvention.Cdecl)] + internal static extern int DisposeTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, int handle, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); [SuppressUnmanagedCodeSecurity] - [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_GetKeyValue_Json", CallingConvention = CallingConvention.Cdecl)] - internal static extern int GetKeyValue(IntPtr dw, [In, Out] ref IntPtr json_data, int handle); + [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_GetKeyValue_Cdr", CallingConvention = CallingConvention.Cdecl)] + internal static extern int GetKeyValue(IntPtr dw, [In, Out] ref IntPtr cdrData, [In, Out] ref UIntPtr size, int handle); #endif } diff --git a/Native/CSharpJsonImplTemplate.txt b/Native/CSharpJsonImplTemplate.txt index ebb729c1..84fe10db 100644 --- a/Native/CSharpJsonImplTemplate.txt +++ b/Native/CSharpJsonImplTemplate.txt @@ -53,6 +53,16 @@ { return JsonSerializer.Deserialize(str, typeof(<%TYPE%>), _serializerContext) as <%TYPE%>; } + + public byte[] EncodeToBytes(<%TYPE%> sample) + { + throw new NotImplementedException(); + } + + public <%TYPE%> DecodeFromBytes(byte[] data) + { + throw new NotImplementedException(); + } #endregion } diff --git a/Native/CWrapperHeaderTemplate.txt b/Native/CWrapperHeaderTemplate.txt index 27e26de8..30ff9f19 100644 --- a/Native/CWrapperHeaderTemplate.txt +++ b/Native/CWrapperHeaderTemplate.txt @@ -21,24 +21,40 @@ EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_Write_Cdr(<%SCOPED%>DataWri EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_WriteWithTimestamp_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data, int handle, ::DDS::Time_t time); +EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_WriteWithTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, int handle, ::DDS::Time_t time); + EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_RegisterInstance_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data); EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_RegisterInstance_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size); EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_RegisterInstanceTimestamp_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data, ::DDS::Time_t time); +EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_RegisterInstanceTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, ::DDS::Time_t time); + EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_LookupInstance_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data); +EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_LookupInstance_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size); + EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_UnregisterInstance_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data, ::DDS::InstanceHandle_t handle); +EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_UnregisterInstance_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, ::DDS::InstanceHandle_t handle); + EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_UnregisterInstanceTimestamp_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data, ::DDS::InstanceHandle_t handle, ::DDS::Time_t time); +EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_UnregisterInstanceTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, ::DDS::InstanceHandle_t handle, ::DDS::Time_t time); + EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_Dispose_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data, int handle); +EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_Dispose_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, int handle); + EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_DisposeTimestamp_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data, int handle, ::DDS::Time_t time); +EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_DisposeTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, int handle, ::DDS::Time_t time); + EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_GetKeyValue_Json(<%SCOPED%>DataWriter_ptr dw, char* & json_data, int handle); +EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_GetKeyValue_Cdr(<%SCOPED%>DataWriter_ptr dw, char* & cdr_data, size_t & size, int handle); + ///////////////////////////////////////////////// // <%TYPE%> DataReader Methods ///////////////////////////////////////////////// diff --git a/Native/CWrapperImplTemplate.txt b/Native/CWrapperImplTemplate.txt index 8955572c..aa55e528 100644 --- a/Native/CWrapperImplTemplate.txt +++ b/Native/CWrapperImplTemplate.txt @@ -59,6 +59,13 @@ int <%SCOPED_METHOD%>DataWriter_WriteWithTimestamp_Json(<%SCOPED%>DataWriter_ptr return dw->write_w_timestamp(sample, handle, time); } +int <%SCOPED_METHOD%>DataWriter_WriteWithTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, int handle, ::DDS::Time_t time) +{ + <%SCOPED%> sample = <%SCOPED_METHOD%>_deserialize_from_bytes(cdr_data, size); + + return dw->write_w_timestamp(sample, handle, time); +} + int <%SCOPED_METHOD%>DataWriter_RegisterInstance_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data) { <%SCOPED%>_var samplev = <%SCOPED_METHOD%>_DecodeJsonSample(json_data); @@ -90,6 +97,13 @@ int <%SCOPED_METHOD%>DataWriter_RegisterInstanceTimestamp_Json(<%SCOPED%>DataWri return dw->register_instance_w_timestamp(sample, time); } +int <%SCOPED_METHOD%>DataWriter_RegisterInstanceTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, ::DDS::Time_t time) +{ + <%SCOPED%> sample = <%SCOPED_METHOD%>_deserialize_from_bytes(cdr_data, size); + + return dw->register_instance_w_timestamp(sample, time); +} + int <%SCOPED_METHOD%>DataWriter_UnregisterInstance_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data, ::DDS::InstanceHandle_t handle) { <%SCOPED%>_var samplev = <%SCOPED_METHOD%>_DecodeJsonSample(json_data); @@ -102,6 +116,13 @@ int <%SCOPED_METHOD%>DataWriter_UnregisterInstance_Json(<%SCOPED%>DataWriter_ptr return dw->unregister_instance(sample, handle); } +int <%SCOPED_METHOD%>DataWriter_UnregisterInstance_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, ::DDS::InstanceHandle_t handle) +{ + <%SCOPED%> sample = <%SCOPED_METHOD%>_deserialize_from_bytes(cdr_data, size); + + return dw->unregister_instance(sample, handle); +} + int <%SCOPED_METHOD%>DataWriter_UnregisterInstanceTimestamp_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data, ::DDS::InstanceHandle_t handle, ::DDS::Time_t time) { <%SCOPED%>_var samplev = <%SCOPED_METHOD%>_DecodeJsonSample(json_data); @@ -114,6 +135,13 @@ int <%SCOPED_METHOD%>DataWriter_UnregisterInstanceTimestamp_Json(<%SCOPED%>DataW return dw->unregister_instance_w_timestamp(sample, handle, time); } +int <%SCOPED_METHOD%>DataWriter_UnregisterInstanceTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, ::DDS::InstanceHandle_t handle, ::DDS::Time_t time) +{ + <%SCOPED%> sample = <%SCOPED_METHOD%>_deserialize_from_bytes(cdr_data, size); + + return dw->unregister_instance_w_timestamp(sample, handle, time); +} + int <%SCOPED_METHOD%>DataWriter_LookupInstance_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data) { <%SCOPED%>_var samplev = <%SCOPED_METHOD%>_DecodeJsonSample(json_data); @@ -126,6 +154,13 @@ int <%SCOPED_METHOD%>DataWriter_LookupInstance_Json(<%SCOPED%>DataWriter_ptr dw, return dw->lookup_instance(sample); } +int <%SCOPED_METHOD%>DataWriter_LookupInstance_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size) +{ + <%SCOPED%> sample = <%SCOPED_METHOD%>_deserialize_from_bytes(cdr_data, size); + + return dw->lookup_instance(sample); +} + int <%SCOPED_METHOD%>DataWriter_Dispose_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data, int handle) { <%SCOPED%>_var samplev = <%SCOPED_METHOD%>_DecodeJsonSample(json_data); @@ -138,6 +173,13 @@ int <%SCOPED_METHOD%>DataWriter_Dispose_Json(<%SCOPED%>DataWriter_ptr dw, const return dw->dispose(sample, handle); } +int <%SCOPED_METHOD%>DataWriter_Dispose_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, int handle) +{ + <%SCOPED%> sample = <%SCOPED_METHOD%>_deserialize_from_bytes(cdr_data, size); + + return dw->dispose(sample, handle); +} + int <%SCOPED_METHOD%>DataWriter_DisposeTimestamp_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data, int handle, ::DDS::Time_t time) { <%SCOPED%>_var samplev = <%SCOPED_METHOD%>_DecodeJsonSample(json_data); @@ -150,6 +192,13 @@ int <%SCOPED_METHOD%>DataWriter_DisposeTimestamp_Json(<%SCOPED%>DataWriter_ptr d return dw->dispose_w_timestamp(sample, handle, time); } +int <%SCOPED_METHOD%>DataWriter_DisposeTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, int handle, ::DDS::Time_t time) +{ + <%SCOPED%> sample = <%SCOPED_METHOD%>_deserialize_from_bytes(cdr_data, size); + + return dw->dispose_w_timestamp(sample, handle, time); +} + int <%SCOPED_METHOD%>DataWriter_GetKeyValue_Json(<%SCOPED%>DataWriter_ptr dw, char* & json_data, int handle) { <%SCOPED%> sample_key; @@ -165,6 +214,21 @@ int <%SCOPED_METHOD%>DataWriter_GetKeyValue_Json(<%SCOPED%>DataWriter_ptr dw, ch return ret; } +int <%SCOPED_METHOD%>DataWriter_GetKeyValue_Cdr(<%SCOPED%>DataWriter_ptr dw, char* & cdr_data, size_t & size, int handle) +{ + <%SCOPED%> sample_key; + ::DDS::ReturnCode_t ret = dw->get_key_value(sample_key, handle); + + if (ret == ::DDS::RETCODE_OK) + { + <%SCOPED%> sample; + <%SCOPED_METHOD%>_CopyKeys(&sample_key, &sample); + <%SCOPED_METHOD%>_serialize_to_bytes(sample, cdr_data, size); + } + + return ret; +} + <%SCOPED%>DataReader_ptr <%SCOPED_METHOD%>DataReader_Narrow(DDS::DataReader_ptr dr) { return <%SCOPED%>DataReader::_narrow(dr); diff --git a/Sources/OpenDDSharp/DDS/DataWriter.cs b/Sources/OpenDDSharp/DDS/DataWriter.cs index 45685feb..2a19efb6 100644 --- a/Sources/OpenDDSharp/DDS/DataWriter.cs +++ b/Sources/OpenDDSharp/DDS/DataWriter.cs @@ -174,7 +174,7 @@ public ReturnCode SetListener(DataWriterListener listener, StatusMask mask) /// /// This operation is intended to be used only if the has /// configured . - /// Otherwise the operation will return immediately with . + /// Otherwise, the operation will return immediately with . /// A return value of indicates that all the samples /// written have been acknowledged by all reliable matched data readers; a return value of /// indicates that maxWait @@ -256,7 +256,7 @@ public ReturnCode GetPublicationMatchedStatus(ref PublicationMatchedStatus statu /// /// or . Otherwise, it has no effect. /// NOTE: Writing data via the write operation on a asserts liveliness on - /// the itself and its . Consequently the use of + /// the itself and its . Consequently, the use of /// AssertLiveliness is only needed if the application is not writing data regularly. /// /// The that indicates the operation result. @@ -372,7 +372,7 @@ private Publisher GetPublisher() else { publisher = new Publisher(ptrPublisher); - EntityManager.Instance.Add((publisher as Entity).ToNative(), publisher); + EntityManager.Instance.Add(((Entity)publisher).ToNative(), publisher); } } diff --git a/Sources/OpenDDSharp/DDS/ITypeSupport.cs b/Sources/OpenDDSharp/DDS/ITypeSupport.cs index 9f708a0c..5aac0d55 100644 --- a/Sources/OpenDDSharp/DDS/ITypeSupport.cs +++ b/Sources/OpenDDSharp/DDS/ITypeSupport.cs @@ -76,4 +76,18 @@ public interface ITypeSupport : ITypeSupport /// The sample JSON representation. /// The decoded sample. T DecodeFromString(string encoded); + + /// + /// Encodes a sample into a byte array using CDR format. + /// + /// The sample to be encoded. + /// The CDR sample representation. + byte[] EncodeToBytes(T sample); + + /// + /// Decodes a CDR byte array into a sample. + /// + /// The sample CDR representation. + /// The decoded sample. + T DecodeFromBytes(byte[] encoded); } \ No newline at end of file diff --git a/Tests/OpenDDSharp.UnitTest/DataWriterCDRTest.cs b/Tests/OpenDDSharp.UnitTest/DataWriterCDRTest.cs new file mode 100644 index 00000000..a7081f82 --- /dev/null +++ b/Tests/OpenDDSharp.UnitTest/DataWriterCDRTest.cs @@ -0,0 +1,1294 @@ +/********************************************************************* +This file is part of OpenDDSharp. + +OpenDDSharp is a .NET wrapper for OpenDDS. +Copyright (C) 2018 Jose Morato + +OpenDDSharp is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenDDSharp is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with OpenDDSharp. If not, see . +**********************************************************************/ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using CdrWrapper; +using CdrWrapperInclude; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenDDSharp.DDS; +using OpenDDSharp.UnitTest.Helpers; +using OpenDDSharp.UnitTest.Listeners; + +namespace OpenDDSharp.UnitTest +{ + /// + /// unit test class. + /// + [TestClass] + public class DataWriterCDRTest + { + #region Constants + private const string TEST_CATEGORY = "DataWriterCDR"; + #endregion + + #region Fields + private DomainParticipant _participant; + private Topic _topic; + private Publisher _publisher; + #endregion + + #region Properties + /// + /// Gets or sets the property. + /// + [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global", Justification = "Required by MSTest")] + public TestContext TestContext { get; set; } + #endregion + + #region Initialization/Cleanup + /// + /// The test initializer method. + /// + [TestInitialize] + public void TestInitialize() + { + _participant = AssemblyInitializer.Factory.CreateParticipant(AssemblyInitializer.RTPS_DOMAIN); + Assert.IsNotNull(_participant); + _participant.BindRtpsUdpTransportConfig(); + + var support = new TestIncludeTypeSupport(); + var typeName = support.GetTypeName(); + var result = support.RegisterType(_participant, typeName); + Assert.AreEqual(ReturnCode.Ok, result); + + _topic = _participant.CreateTopic(TestContext.TestName, typeName); + Assert.IsNotNull(_topic); + Assert.IsNull(_topic.Listener); + Assert.AreEqual(TestContext.TestName, _topic.Name); + Assert.AreEqual(typeName, _topic.TypeName); + + _publisher = _participant.CreatePublisher(); + Assert.IsNotNull(_publisher); + } + + /// + /// The test cleanup method. + /// + [TestCleanup] + public void TestCleanup() + { + _publisher?.DeleteContainedEntities(); + _participant?.DeletePublisher(_publisher); + _participant?.DeleteTopic(_topic); + _participant?.DeleteContainedEntities(); + + AssemblyInitializer.Factory?.DeleteParticipant(_participant); + + _participant = null; + _publisher = null; + _topic = null; + } + #endregion + + #region Test Methods + /// + /// Test the properties. + /// + [TestMethod] + [TestCategory(TEST_CATEGORY)] + public void TestProperties() + { + // Create a DataWriter and check the Topic and Participant properties + var writer = _publisher.CreateDataWriter(_topic); + + Assert.IsNotNull(writer); + Assert.IsNotNull(writer.Topic); + Assert.IsNotNull(writer.Publisher); + Assert.AreSame(_topic, writer.Topic); + Assert.AreSame(_publisher, writer.Publisher); + Assert.IsNull(writer.Listener); + + _publisher.DeleteDataWriter(writer); + } + + /// + /// Test the constructor. + /// + [TestMethod] + [TestCategory(TEST_CATEGORY)] + public void TestNewDataWriterQos() + { + var qos = new DataWriterQos(); + TestHelper.TestDefaultDataWriterQos(qos); + } + + /// + /// Test the method. + /// + [TestMethod] + [TestCategory(TEST_CATEGORY)] + public void TestGetQos() + { + // Create a non-default QoS and create a DataWriter with it + var qos = TestHelper.CreateNonDefaultDataWriterQos(); + + var dataWriter = _publisher.CreateDataWriter(_topic, qos); + Assert.IsNotNull(dataWriter); + + // Call GetQos and check the values received + qos = new DataWriterQos(); + var result = dataWriter.GetQos(qos); + Assert.AreEqual(ReturnCode.Ok, result); + TestHelper.TestNonDefaultDataWriterQos(qos); + + // Test GetQos with null parameter + result = dataWriter.GetQos(null); + Assert.AreEqual(ReturnCode.BadParameter, result); + + _publisher.DeleteDataWriter(dataWriter); + } + + /// + /// Test the method. + /// + [TestMethod] + [TestCategory(TEST_CATEGORY)] + public void TestSetQos() + { + // Create a new DataWriter using the default QoS + var dataWriter = _publisher.CreateDataWriter(_topic); + Assert.IsNotNull(dataWriter); + + // Get the qos to ensure that is using the default properties + var qos = new DataWriterQos(); + var result = dataWriter.GetQos(qos); + Assert.AreEqual(ReturnCode.Ok, result); + TestHelper.TestDefaultDataWriterQos(qos); + + // Try to change an immutable property + qos.Ownership.Kind = OwnershipQosPolicyKind.ExclusiveOwnershipQos; + result = dataWriter.SetQos(qos); + Assert.AreEqual(ReturnCode.ImmutablePolicy, result); + + // Change some mutable properties and check them + qos = new DataWriterQos + { + OwnershipStrength = + { + Value = 100, + }, + }; + + result = dataWriter.SetQos(qos); + Assert.AreEqual(ReturnCode.Ok, result); + + qos = new DataWriterQos(); + result = dataWriter.GetQos(qos); + Assert.AreEqual(ReturnCode.Ok, result); + Assert.AreEqual(100, qos.OwnershipStrength.Value); + + // Try to set immutable QoS properties before enable the DataWriter + var pubQos = new PublisherQos + { + EntityFactory = + { + AutoenableCreatedEntities = false, + }, + }; + result = _publisher.SetQos(pubQos); + Assert.AreEqual(ReturnCode.Ok, result); + + var otherDataWriter = _publisher.CreateDataWriter(_topic); + Assert.IsNotNull(otherDataWriter); + + qos = new DataWriterQos + { + Ownership = + { + Kind = OwnershipQosPolicyKind.ExclusiveOwnershipQos, + }, + }; + result = otherDataWriter.SetQos(qos); + Assert.AreEqual(ReturnCode.Ok, result); + + qos = new DataWriterQos(); + result = otherDataWriter.GetQos(qos); + Assert.AreEqual(ReturnCode.Ok, result); + Assert.AreEqual(OwnershipQosPolicyKind.ExclusiveOwnershipQos, qos.Ownership.Kind); + + result = otherDataWriter.Enable(); + Assert.AreEqual(ReturnCode.Ok, result); + + // Set back the default publisher QoS + pubQos = new PublisherQos(); + _publisher.SetQos(pubQos); + Assert.AreEqual(ReturnCode.Ok, result); + + // Test SetQos with null parameter + result = dataWriter.SetQos(null); + Assert.AreEqual(ReturnCode.BadParameter, result); + + Assert.AreEqual(ReturnCode.Ok, _publisher.DeleteDataWriter(dataWriter)); + Assert.AreEqual(ReturnCode.Ok, _publisher.DeleteDataWriter(otherDataWriter)); + } + + /// + /// Test the method. + /// + [TestMethod] + [TestCategory(TEST_CATEGORY)] + public void TestGetListener() + { + // Create a new DataWriter with a listener + var listener = new MyDataWriterListener(); + var dataWriter = _publisher.CreateDataWriter(_topic, null, listener); + Assert.IsNotNull(dataWriter); + + // Call to GetListener and check the listener received +#pragma warning disable CS0618 // Type or member is obsolete + var received = (MyDataWriterListener)dataWriter.GetListener(); +#pragma warning restore CS0618 // Type or member is obsolete + + Assert.IsNotNull(received); + Assert.AreSame(listener, received); + + Assert.AreEqual(ReturnCode.Ok, dataWriter.SetListener(null)); + listener.Dispose(); + + _publisher.DeleteDataWriter(dataWriter); + } + + /// + /// Test the method. + /// + [TestMethod] + [TestCategory(TEST_CATEGORY)] + public void TestSetListener() + { + // Create a new DataWriter without listener + var dataWriter = _publisher.CreateDataWriter(_topic); + Assert.IsNotNull(dataWriter); + + var listener = (MyDataWriterListener)dataWriter.Listener; + Assert.IsNull(listener); + + // Create a listener, set it and check that is correctly set + listener = new MyDataWriterListener(); + var result = dataWriter.SetListener(listener, StatusMask.AllStatusMask); + Assert.AreEqual(ReturnCode.Ok, result); + + var received = (MyDataWriterListener)dataWriter.Listener; + Assert.IsNotNull(received); + Assert.AreEqual(listener, received); + + // Remove the listener calling SetListener with null and check it + result = dataWriter.SetListener(null, StatusMask.NoStatusMask); + Assert.AreEqual(ReturnCode.Ok, result); + + listener.Dispose(); + + received = (MyDataWriterListener)dataWriter.Listener; + Assert.IsNull(received); + + _publisher.DeleteDataWriter(dataWriter); + } + + /// + /// Test the method. + /// + [TestMethod] + [TestCategory(TEST_CATEGORY)] + public void TestWaitForAcknowledgments() + { + using var evt = new ManualResetEventSlim(false); + + // Initialize entities + var writer = _publisher.CreateDataWriter(_topic); + Assert.IsNotNull(writer); + var dataWriter = new TestIncludeDataWriter(writer); + + var subscriber = _participant.CreateSubscriber(); + Assert.IsNotNull(subscriber); + + var drQos = new DataReaderQos + { + Reliability = + { + Kind = ReliabilityQosPolicyKind.ReliableReliabilityQos, + }, + }; + var reader = subscriber.CreateDataReader(_topic, drQos); + Assert.IsNotNull(reader); + + Assert.IsTrue(reader.WaitForPublications(1, 5_000)); + Assert.IsTrue(writer.WaitForSubscriptions(1, 5_000)); + + var statusCondition = reader.StatusCondition; + statusCondition.EnabledStatuses = StatusKind.DataAvailableStatus; + + // Write some instances and wait for acknowledgments + for (var i = 0; i < 10; i++) + { + evt.Reset(); + TestHelper.CreateWaitSetThread(evt, statusCondition); + + var result = dataWriter.Write(new TestInclude + { + Id = i.ToString(), + }); + Assert.AreEqual(ReturnCode.Ok, result); + + result = dataWriter.WaitForAcknowledgments(new Duration { Seconds = 5 }); + Assert.AreEqual(ReturnCode.Ok, result); + + Assert.IsTrue(evt.Wait(5_000)); + } + + Assert.AreEqual(ReturnCode.Ok, reader.DeleteContainedEntities()); + Assert.AreEqual(ReturnCode.Ok, subscriber.DeleteDataReader(reader)); + Assert.AreEqual(ReturnCode.Ok, subscriber.DeleteContainedEntities()); + Assert.AreEqual(ReturnCode.Ok, _publisher.DeleteDataWriter(writer)); + Assert.AreEqual(ReturnCode.Ok, _participant.DeleteSubscriber(subscriber)); + } + + /// + /// Test the method. + /// + [TestMethod] + [TestCategory(TEST_CATEGORY)] + public void TestGetLivelinessLostStatus() + { + using var evt = new ManualResetEventSlim(false); + + // Initialize entities + var dwQos = new DataWriterQos + { + Liveliness = + { + Kind = LivelinessQosPolicyKind.ManualByTopicLivelinessQos, + LeaseDuration = new Duration { Seconds = 1 }, + }, + }; + + var writer = _publisher.CreateDataWriter(_topic, dwQos); + Assert.IsNotNull(writer); + var dataWriter = new TestIncludeDataWriter(writer); + Assert.IsNotNull(dataWriter); + + var result = writer.AssertLiveliness(); + Assert.AreEqual(ReturnCode.Ok, result); + + // Create a reader + var subscriber = _participant.CreateSubscriber(); + Assert.IsNotNull(subscriber); + + var reader = subscriber.CreateDataReader(_topic); + Assert.IsNotNull(reader); + + var statusCondition = writer.StatusCondition; + statusCondition.EnabledStatuses = StatusKind.LivelinessLostStatus; + TestHelper.CreateWaitSetThread(evt, statusCondition); + + Assert.IsTrue(reader.WaitForPublications(1, 1_500)); + Assert.IsTrue(writer.WaitForSubscriptions(1, 1_500)); + + result = writer.AssertLiveliness(); + Assert.AreEqual(ReturnCode.Ok, result); + + // After half second liveliness should not be lost yet + Assert.IsFalse(evt.Wait(500)); + + LivelinessLostStatus status = default; + result = writer.GetLivelinessLostStatus(ref status); + Assert.AreEqual(ReturnCode.Ok, result); + Assert.AreEqual(0, status.TotalCount); + Assert.AreEqual(0, status.TotalCountChange); + + // After one second and a half one liveliness should be lost + Assert.IsTrue(evt.Wait(1_500)); + + result = writer.GetLivelinessLostStatus(ref status); + Assert.AreEqual(ReturnCode.Ok, result); + Assert.AreEqual(1, status.TotalCount); + Assert.AreEqual(1, status.TotalCountChange); + + Assert.AreEqual(ReturnCode.Ok, reader.DeleteContainedEntities()); + Assert.AreEqual(ReturnCode.Ok, subscriber.DeleteDataReader(reader)); + Assert.AreEqual(ReturnCode.Ok, subscriber.DeleteContainedEntities()); + Assert.AreEqual(ReturnCode.Ok, _publisher.DeleteDataWriter(writer)); + Assert.AreEqual(ReturnCode.Ok, _participant.DeleteSubscriber(subscriber)); + } + + /// + /// Test the method. + /// + [TestMethod] + [TestCategory(TEST_CATEGORY)] + public void TestGetOfferedDeadlineMissedStatus() + { + using var evt = new ManualResetEventSlim(false); + + // Initialize entities + var qos = new DataWriterQos + { + Deadline = + { + Period = new Duration + { + Seconds = 1, + }, + }, + }; + var writer = _publisher.CreateDataWriter(_topic, qos); + Assert.IsNotNull(writer); + var dataWriter = new TestIncludeDataWriter(writer); + + var statusCondition = writer.StatusCondition; + statusCondition.EnabledStatuses = StatusKind.OfferedDeadlineMissedStatus; + TestHelper.CreateWaitSetThread(evt, statusCondition); + + var subscriber = _participant.CreateSubscriber(); + Assert.IsNotNull(subscriber); + + var reader = subscriber.CreateDataReader(_topic); + Assert.IsNotNull(reader); + + // Wait for discovery and write an instance + var found = writer.WaitForSubscriptions(1, 1000); + Assert.IsTrue(found); + + found = reader.WaitForPublications(1, 1000); + Assert.IsTrue(found); + + dataWriter.Write(new TestInclude + { + Id = "1", + }); + + // After half second deadline should not be lost yet + Assert.IsFalse(evt.Wait(500)); + + OfferedDeadlineMissedStatus status = default; + var result = writer.GetOfferedDeadlineMissedStatus(ref status); + Assert.AreEqual(ReturnCode.Ok, result); + Assert.AreEqual(0, status.TotalCount); + Assert.AreEqual(0, status.TotalCountChange); + Assert.AreEqual(InstanceHandle.HandleNil, status.LastInstanceHandle); + + // After one second and a half one deadline should be lost + Assert.IsTrue(evt.Wait(1_500)); + + result = writer.GetOfferedDeadlineMissedStatus(ref status); + Assert.AreEqual(ReturnCode.Ok, result); + Assert.AreEqual(1, status.TotalCount); + Assert.AreEqual(1, status.TotalCountChange); + Assert.AreNotEqual(InstanceHandle.HandleNil, status.LastInstanceHandle); + + Assert.AreEqual(ReturnCode.Ok, reader.DeleteContainedEntities()); + Assert.AreEqual(ReturnCode.Ok, subscriber.DeleteDataReader(reader)); + Assert.AreEqual(ReturnCode.Ok, subscriber.DeleteContainedEntities()); + Assert.AreEqual(ReturnCode.Ok, _publisher.DeleteDataWriter(writer)); + Assert.AreEqual(ReturnCode.Ok, _participant.DeleteSubscriber(subscriber)); + } + + /// + /// Test the method. + /// + [TestMethod] + [TestCategory(TEST_CATEGORY)] + public void TestGetOfferedIncompatibleQosStatus() + { + using var evt = new ManualResetEventSlim(false); + + // Initialize entities + var qos = new DataWriterQos + { + Reliability = + { + Kind = ReliabilityQosPolicyKind.BestEffortReliabilityQos, + }, + }; + var writer = _publisher.CreateDataWriter(_topic, qos); + Assert.IsNotNull(writer); + + var statusCondition = writer.StatusCondition; + statusCondition.EnabledStatuses = StatusKind.OfferedIncompatibleQosStatus; + TestHelper.CreateWaitSetThread(evt, statusCondition); + + // If not matched readers should return the default status + OfferedIncompatibleQosStatus status = default; + var result = writer.GetOfferedIncompatibleQosStatus(ref status); + Assert.AreEqual(ReturnCode.Ok, result); + Assert.AreEqual(0, status.TotalCount); + Assert.AreEqual(0, status.TotalCountChange); + Assert.IsNotNull(status.Policies); + Assert.AreEqual(0, status.Policies.Count); + Assert.AreEqual(0, status.LastPolicyId); + + // Create a not compatible reader + var subscriber = _participant.CreateSubscriber(); + Assert.IsNotNull(subscriber); + + var drQos = new DataReaderQos + { + Reliability = + { + Kind = ReliabilityQosPolicyKind.ReliableReliabilityQos, + }, + }; + var reader = subscriber.CreateDataReader(_topic, drQos); + Assert.IsNotNull(reader); + + // Wait for discovery and check the status + Assert.IsTrue(evt.Wait(1_500)); + + status = default; + result = writer.GetOfferedIncompatibleQosStatus(ref status); + Assert.AreEqual(ReturnCode.Ok, result); + Assert.AreEqual(1, status.TotalCount); + Assert.AreEqual(1, status.TotalCountChange); + Assert.AreEqual(11, status.LastPolicyId); + Assert.IsNotNull(status.Policies); + Assert.AreEqual(1, status.Policies.Count); + Assert.AreEqual(1, status.Policies.First().Count); + Assert.AreEqual(11, status.Policies.First().PolicyId); + + Assert.AreEqual(ReturnCode.Ok, reader.DeleteContainedEntities()); + Assert.AreEqual(ReturnCode.Ok, subscriber.DeleteDataReader(reader)); + Assert.AreEqual(ReturnCode.Ok, subscriber.DeleteContainedEntities()); + Assert.AreEqual(ReturnCode.Ok, _participant.DeleteSubscriber(subscriber)); + Assert.AreEqual(ReturnCode.Ok, _publisher.DeleteDataWriter(writer)); + } + + /// + /// Test the method. + /// + [TestMethod] + [TestCategory(TEST_CATEGORY)] + public void TestGetPublicationMatchedStatus() + { + using var evt = new ManualResetEventSlim(false); + + // Initialize entities + var qos = new DataWriterQos + { + Reliability = + { + Kind = ReliabilityQosPolicyKind.BestEffortReliabilityQos, + }, + }; + var writer = _publisher.CreateDataWriter(_topic, qos); + Assert.IsNotNull(writer); + + var statusCondition = writer.StatusCondition; + statusCondition.EnabledStatuses = StatusKind.PublicationMatchedStatus; + TestHelper.CreateWaitSetThread(evt, statusCondition); + + // If not DataReaders are created should return the default status + PublicationMatchedStatus status = default; + var result = writer.GetPublicationMatchedStatus(ref status); + Assert.AreEqual(ReturnCode.Ok, result); + Assert.AreEqual(0, status.CurrentCount); + Assert.AreEqual(0, status.CurrentCountChange); + Assert.AreEqual(0, status.TotalCount); + Assert.AreEqual(0, status.TotalCountChange); + Assert.AreEqual(InstanceHandle.HandleNil, status.LastSubscriptionHandle); + + // Create a not compatible reader + var subscriber = _participant.CreateSubscriber(); + Assert.IsNotNull(subscriber); + + var drQos = new DataReaderQos + { + Reliability = + { + Kind = ReliabilityQosPolicyKind.ReliableReliabilityQos, + }, + }; + var reader = subscriber.CreateDataReader(_topic, drQos); + Assert.IsNotNull(reader); + + // Wait for discovery and check the status + Assert.IsFalse(evt.Wait(1_500)); + + result = writer.GetPublicationMatchedStatus(ref status); + Assert.AreEqual(ReturnCode.Ok, result); + Assert.AreEqual(0, status.CurrentCount); + Assert.AreEqual(0, status.CurrentCountChange); + Assert.AreEqual(0, status.TotalCount); + Assert.AreEqual(0, status.TotalCountChange); + Assert.AreEqual(InstanceHandle.HandleNil, status.LastSubscriptionHandle); + + // Create a compatible reader + var otherReader = subscriber.CreateDataReader(_topic); + Assert.IsNotNull(otherReader); + + // Wait for discovery and check the status + Assert.IsTrue(evt.Wait(1_500)); + + result = writer.GetPublicationMatchedStatus(ref status); + Assert.AreEqual(ReturnCode.Ok, result); + Assert.AreEqual(1, status.CurrentCount); + Assert.AreEqual(1, status.CurrentCountChange); + Assert.AreEqual(1, status.TotalCount); + Assert.AreEqual(1, status.TotalCountChange); + Assert.AreEqual(otherReader.InstanceHandle, status.LastSubscriptionHandle); + + Assert.AreEqual(ReturnCode.Ok, reader.DeleteContainedEntities()); + Assert.AreEqual(ReturnCode.Ok, otherReader.DeleteContainedEntities()); + Assert.AreEqual(ReturnCode.Ok, subscriber.DeleteDataReader(reader)); + Assert.AreEqual(ReturnCode.Ok, subscriber.DeleteDataReader(otherReader)); + Assert.AreEqual(ReturnCode.Ok, subscriber.DeleteContainedEntities()); + Assert.AreEqual(ReturnCode.Ok, _participant.DeleteSubscriber(subscriber)); + Assert.AreEqual(ReturnCode.Ok, _publisher.DeleteDataWriter(writer)); + } + + /// + /// Test the method. + /// + [TestMethod] + [TestCategory(TEST_CATEGORY)] + public void TestAssertLiveliness() + { + // Initialize entities + var qos = new DataWriterQos + { + Liveliness = + { + Kind = LivelinessQosPolicyKind.ManualByTopicLivelinessQos, + LeaseDuration = new Duration + { + Seconds = 1, + }, + }, + }; + var writer = _publisher.CreateDataWriter(_topic, qos); + Assert.IsNotNull(writer); + + // Manually assert liveliness + for (var i = 0; i < 5; i++) + { + var assertResult = writer.AssertLiveliness(); + Assert.AreEqual(ReturnCode.Ok, assertResult); + } + + // Check that no liveliness has been lost + LivelinessLostStatus status = default; + var result = writer.GetLivelinessLostStatus(ref status); + Assert.AreEqual(ReturnCode.Ok, result); + Assert.AreEqual(0, status.TotalCount); + Assert.AreEqual(0, status.TotalCountChange); + + Assert.AreEqual(ReturnCode.Ok, writer.AssertLiveliness()); + Assert.AreEqual(ReturnCode.Ok, _publisher.DeleteDataWriter(writer)); + } + + /// + /// Test the method. + /// + [TestMethod] + [TestCategory(TEST_CATEGORY)] + public void TestGetMatchedSubscriptions() + { + using var evt = new ManualResetEventSlim(false); + + // Initialize entities + var qos = new DataWriterQos + { + Reliability = + { + Kind = ReliabilityQosPolicyKind.BestEffortReliabilityQos, + }, + }; + var writer = _publisher.CreateDataWriter(_topic, qos); + Assert.IsNotNull(writer); + + var statusCondition = writer.StatusCondition; + statusCondition.EnabledStatuses = StatusKind.PublicationMatchedStatus; + TestHelper.CreateWaitSetThread(evt, statusCondition); + + // Test matched subscriptions without any match + var list = new List + { + InstanceHandle.HandleNil, + }; + var result = writer.GetMatchedSubscriptions(list); + Assert.AreEqual(ReturnCode.Ok, result); + Assert.AreEqual(0, list.Count); + + // Create a not compatible reader + var subscriber = _participant.CreateSubscriber(); + Assert.IsNotNull(subscriber); + + var drQos = new DataReaderQos + { + Reliability = + { + Kind = ReliabilityQosPolicyKind.ReliableReliabilityQos, + }, + }; + var reader = subscriber.CreateDataReader(_topic, drQos); + Assert.IsNotNull(reader); + + // Wait for discovery and check the matched subscriptions + Assert.IsFalse(evt.Wait(1_500)); + + result = writer.GetMatchedSubscriptions(list); + Assert.AreEqual(ReturnCode.Ok, result); + Assert.AreEqual(0, list.Count); + + // Create a compatible reader + var otherReader = subscriber.CreateDataReader(_topic); + Assert.IsNotNull(otherReader); + + // Wait for discovery and check the matched subscriptions + Assert.IsTrue(evt.Wait(1_500)); + + result = writer.GetMatchedSubscriptions(list); + Assert.AreEqual(ReturnCode.Ok, result); + Assert.AreEqual(1, list.Count); + Assert.AreEqual(otherReader.InstanceHandle, list[0]); + + // Test with null parameter + result = writer.GetMatchedSubscriptions(null); + Assert.AreEqual(ReturnCode.BadParameter, result); + + _publisher.DeleteDataWriter(writer); + reader.DeleteContainedEntities(); + otherReader.DeleteContainedEntities(); + subscriber.DeleteDataReader(reader); + subscriber.DeleteDataReader(otherReader); + subscriber.DeleteContainedEntities(); + _participant.DeleteSubscriber(subscriber); + } + + /// + /// Test the method. + /// + [TestMethod] + [TestCategory(TEST_CATEGORY)] + public void TestGetMatchedSubscriptionData() + { + // Initialize entities + var dwQos = TestHelper.CreateNonDefaultDataWriterQos(); + dwQos.Reliability.Kind = ReliabilityQosPolicyKind.ReliableReliabilityQos; + var writer = _publisher.CreateDataWriter(_topic, dwQos); + Assert.IsNotNull(writer); + + // DCPSInfoRepo-based discovery generates Built-In Topic data once (inside the + // info repo process) and therefore all known entities in the domain are + // reflected in the Built-In Topics. RTPS discovery, on the other hand, follows + // the DDS specification and omits "local" entities from the Built-In Topics. + // The definition of "local" means those entities belonging to the same Domain + // Participant as the given Built-In Topic Subscriber. + // https://github.com/OpenDDS/OpenDDS/blob/master/docs/design/RTPS + + // OPENDDS ISSUE: GetMatchedSubscriptions returns local entities but GetMatchedSubscriptionData doesn't + // because it is looking in the Built-in topic. If not found in the built-in, shouldn't try to look locally? + // WORKAROUND: Create another participant for the DataReader. + var otherParticipant = AssemblyInitializer.Factory.CreateParticipant(AssemblyInitializer.RTPS_DOMAIN); + Assert.IsNotNull(otherParticipant); + otherParticipant.BindRtpsUdpTransportConfig(); + + var support = new TestIncludeTypeSupport(); + var typeName = support.GetTypeName(); + var result = support.RegisterType(otherParticipant, typeName); + Assert.AreEqual(ReturnCode.Ok, result); + + var otherTopic = otherParticipant.CreateTopic(nameof(TestGetMatchedSubscriptionData), typeName); + Assert.IsNotNull(otherTopic); + + var subscriber = otherParticipant.CreateSubscriber(); + Assert.IsNotNull(subscriber); + + var drQos = TestHelper.CreateNonDefaultDataReaderQos(); + var reader = subscriber.CreateDataReader(otherTopic, drQos); + Assert.IsNotNull(reader); + + // Wait for subscriptions/publications + var found = writer.WaitForSubscriptions(1, 5000); + Assert.IsTrue(found); + found = reader.WaitForPublications(1, 5000); + Assert.IsTrue(found); + + // Get the matched subscriptions + var list = new List(); + result = writer.GetMatchedSubscriptions(list); + Assert.AreEqual(ReturnCode.Ok, result); + Assert.AreEqual(1, list.Count); + + // Get the matched subscription data + SubscriptionBuiltinTopicData data = default; + result = writer.GetMatchedSubscriptionData(list[0], ref data); + Assert.AreEqual(ReturnCode.Ok, result); + TestHelper.TestNonDefaultSubscriptionData(data); + + // Destroy entities + Assert.AreEqual(ReturnCode.Ok, reader.DeleteContainedEntities()); + Assert.AreEqual(ReturnCode.Ok, subscriber.DeleteDataReader(reader)); + Assert.AreEqual(ReturnCode.Ok, subscriber.DeleteContainedEntities()); + Assert.AreEqual(ReturnCode.Ok, otherParticipant.DeleteSubscriber(subscriber)); + Assert.AreEqual(ReturnCode.Ok, _publisher.DeleteDataWriter(writer)); + Assert.AreEqual(ReturnCode.Ok, otherParticipant.DeleteTopic(otherTopic)); + Assert.AreEqual(ReturnCode.Ok, AssemblyInitializer.Factory.DeleteParticipant(otherParticipant)); + } + + /// + /// Test the method. + /// + [TestMethod] + [TestCategory(TEST_CATEGORY)] + public void TestRegisterInstance() + { + // Initialize entities + var writer = _publisher.CreateDataWriter(_topic); + Assert.IsNotNull(writer); + var dataWriter = new TestIncludeDataWriter(writer); + + // Register an instance + var handle = dataWriter.RegisterInstance(new TestInclude { Id = "1" }); + Assert.AreNotEqual(InstanceHandle.HandleNil, handle); + + // Register an instance with timestamp + var otherHandle = dataWriter.RegisterInstance(new TestInclude { Id = "2" }, DateTime.Now.ToTimestamp()); + Assert.AreNotEqual(InstanceHandle.HandleNil, otherHandle); + Assert.AreNotEqual(handle, otherHandle); + + Assert.AreEqual(ReturnCode.Ok, _publisher.DeleteDataWriter(writer)); + } + + /// + /// Test the method. + /// + [TestMethod] + [TestCategory(TEST_CATEGORY)] + public void TestUnregisterInstance() + { + // Initialize entities + var writer = _publisher.CreateDataWriter(_topic); + Assert.IsNotNull(writer); + var dataWriter = new TestIncludeDataWriter(writer); + + // Unregister not registered instance + var result = dataWriter.UnregisterInstance(new TestInclude { Id = "1" }); + Assert.AreEqual(ReturnCode.PreconditionNotMet, result); + + // Register an instance + var instance1 = new TestInclude { Id = "1" }; + var handle1 = dataWriter.RegisterInstance(instance1); + Assert.AreNotEqual(InstanceHandle.HandleNil, handle1); + + // Unregister the previous registered instance with the simplest overload + result = dataWriter.UnregisterInstance(new TestInclude { Id = "1" }); + Assert.AreEqual(ReturnCode.Ok, result); + + // Register a new instance + var instance2 = new TestInclude { Id = "2" }; + var handle2 = dataWriter.RegisterInstance(instance2); + Assert.AreNotEqual(InstanceHandle.HandleNil, handle2); + + // Unregister the previous registered instance with the handle + result = dataWriter.UnregisterInstance(new TestInclude { Id = "2" }, handle2); + Assert.AreEqual(ReturnCode.Ok, result); + + // Register a new instance + var instance3 = new TestInclude { Id = "3" }; + var handle3 = dataWriter.RegisterInstance(instance3); + Assert.AreNotEqual(InstanceHandle.HandleNil, handle3); + + // Unregister the previous registered instance with the handle and the timestamp + result = dataWriter.UnregisterInstance(new TestInclude { Id = "3" }, handle3, DateTime.Now.ToTimestamp()); + Assert.AreEqual(ReturnCode.Ok, result); + + Assert.AreEqual(ReturnCode.Ok, _publisher.DeleteDataWriter(writer)); + } + + /// + /// Test the method. + /// + [TestMethod] + [TestCategory(TEST_CATEGORY)] + public void TestWrite() + { + var evt = new ManualResetEventSlim(false); + + // Initialize entities + var duration = new Duration { Seconds = 5 }; + + var writer = _publisher.CreateDataWriter(_topic); + Assert.IsNotNull(writer); + var dataWriter = new TestIncludeDataWriter(writer); + + var subscriber = _participant.CreateSubscriber(); + Assert.IsNotNull(subscriber); + + var qos = new DataReaderQos + { + Reliability = + { + Kind = ReliabilityQosPolicyKind.ReliableReliabilityQos, + }, + }; + var listener = new MyDataReaderListener(); + var dataReader = subscriber.CreateDataReader(_topic, qos, listener); + Assert.IsNotNull(dataReader); + + var count = 0; + Timestamp timestamp = default; + var samples = new List(); + var infos = new List(); + DataReader receivedReader = null; + + listener.DataAvailable += (reader) => + { + count++; + receivedReader = reader; + + evt.Set(); + }; + + // Wait for discovery + var found = writer.WaitForSubscriptions(1, 1000); + Assert.IsTrue(found); + + found = dataReader.WaitForPublications(1, 1000); + Assert.IsTrue(found); + + // Write an instance with the simplest overload + var result = dataWriter.Write(new TestInclude { Id = "1" }); + Assert.AreEqual(ReturnCode.Ok, result); + + result = dataWriter.WaitForAcknowledgments(duration); + Assert.AreEqual(ReturnCode.Ok, result); + + Assert.IsTrue(evt.Wait(1_500)); + Assert.AreEqual(1, count); + + evt.Reset(); + + // Write an instance with the handle parameter as HandleNil + result = dataWriter.Write(new TestInclude { Id = "2" }, InstanceHandle.HandleNil); + Assert.AreEqual(ReturnCode.Ok, result); + + result = dataWriter.WaitForAcknowledgments(duration); + Assert.AreEqual(ReturnCode.Ok, result); + + Assert.IsTrue(evt.Wait(1_500)); + Assert.AreEqual(2, count); + evt.Reset(); + + // Write an instance with the handle parameter with a previously registered instance + var instance = new TestInclude { Id = "3" }; + var handle = dataWriter.RegisterInstance(instance); + Assert.AreNotEqual(InstanceHandle.HandleNil, handle); + + result = dataWriter.Write(instance, handle); + Assert.AreEqual(ReturnCode.Ok, result); + + result = dataWriter.WaitForAcknowledgments(duration); + Assert.AreEqual(ReturnCode.Ok, result); + + Assert.IsTrue(evt.Wait(1_500)); + Assert.AreEqual(3, count); + evt.Reset(); + + // Write an instance with the handle parameter and the timestamp + var now = DateTime.Now.ToTimestamp(); + var instance1 = new TestInclude { Id = "4" }; + var handle1 = dataWriter.RegisterInstance(instance1); + Assert.AreNotEqual(InstanceHandle.HandleNil, handle1); + + result = dataWriter.Write(instance1, handle1, now); + Assert.AreEqual(ReturnCode.Ok, result); + + result = dataWriter.WaitForAcknowledgments(duration); + Assert.AreEqual(ReturnCode.Ok, result); + + Assert.IsTrue(evt.Wait(1_500)); + Assert.AreEqual(4, count); + Assert.IsNotNull(receivedReader); + + var dr = new TestIncludeDataReader(receivedReader); + + var lookupHandle = dr.LookupInstance(new TestInclude { Id = count.ToString() }); + var retReadInstance = dr.ReadInstance(samples, infos, lookupHandle); + if (retReadInstance == ReturnCode.Ok && infos.Count > 0) + { + timestamp = infos[0].SourceTimestamp; + } + + Assert.AreNotEqual(InstanceHandle.HandleNil, lookupHandle); + Assert.AreEqual(ReturnCode.Ok, retReadInstance); + Assert.IsNotNull(infos); + Assert.AreEqual(1, infos.Count); + Assert.AreEqual(now.Seconds, timestamp.Seconds); + Assert.AreEqual(now.NanoSeconds, timestamp.NanoSeconds); + + foreach (var d in listener.DataAvailable.GetInvocationList()) + { + var del = (Action)d; + listener.DataAvailable -= del; + } + Assert.AreEqual(ReturnCode.Ok, dataReader.SetListener(null, StatusMask.NoStatusMask)); + listener.Dispose(); + + evt.Dispose(); + + Assert.AreEqual(ReturnCode.Ok, _publisher.DeleteDataWriter(writer)); + Assert.AreEqual(ReturnCode.Ok, dataReader.DeleteContainedEntities()); + Assert.AreEqual(ReturnCode.Ok, subscriber.DeleteDataReader(dataReader)); + Assert.AreEqual(ReturnCode.Ok, subscriber.DeleteContainedEntities()); + Assert.AreEqual(ReturnCode.Ok, _participant.DeleteSubscriber(subscriber)); + } + + /// + /// Test the method. + /// + [TestMethod] + [TestCategory(TEST_CATEGORY)] + public void TestDispose() + { + // Initialize entities + var qos = new DataWriterQos + { + WriterDataLifecycle = + { + AutodisposeUnregisteredInstances = false, + }, + }; + var writer = _publisher.CreateDataWriter(_topic, qos); + Assert.IsNotNull(writer); + var dataWriter = new TestIncludeDataWriter(writer); + + var subscriber = _participant.CreateSubscriber(); + Assert.IsNotNull(subscriber); + + var drQos = new DataReaderQos(); + qos.Reliability.Kind = ReliabilityQosPolicyKind.ReliableReliabilityQos; + var dataReader = subscriber.CreateDataReader(_topic, drQos); + Assert.IsNotNull(dataReader); + var statusCondition = dataReader.StatusCondition; + statusCondition.EnabledStatuses = StatusKind.DataAvailableStatus; + + var dr = new TestIncludeDataReader(dataReader); + + var evtDisposed = new ManualResetEventSlim(false); + var evtAlive = new ManualResetEventSlim(false); + + var countDisposed = 0; + Timestamp timestamp = default; + var waitSet = new WaitSet(); + waitSet.AttachCondition(statusCondition); + var thread = new Thread(() => + { + var isSet = false; + while (!isSet) + { + ICollection conditions = new List(); + waitSet.Wait(conditions); + + if (!conditions.Any(cond => cond == statusCondition && cond.TriggerValue)) + { + continue; + } + + var samples = new List(); + var infos = new List(); + var ret = dr.Take(samples, infos); + if (ret != ReturnCode.Ok || !infos.Any()) + { + continue; + } + + foreach (var info in infos) + { + if (info.InstanceState == InstanceStateKind.NotAliveDisposedInstanceState) + { + countDisposed++; + if (countDisposed == 3) + { + timestamp = info.SourceTimestamp; + isSet = true; + } + + evtDisposed.Set(); + } + else if (info.InstanceState == InstanceStateKind.AliveInstanceState) + { + evtAlive.Set(); + } + } + } + }) + { + IsBackground = true, + }; + thread.Start(); + + // Wait for discovery + Assert.IsTrue(writer.WaitForSubscriptions(1, 10_000)); + Assert.IsTrue(dataReader.WaitForPublications(1, 10_000)); + + // Dispose an instance that does not exist + var result = dataWriter.Dispose(new TestInclude { Id = "1" }, InstanceHandle.HandleNil); + Assert.AreEqual(ReturnCode.Error, result); + + // Call dispose with the simplest overload + var instance1 = new TestInclude { Id = "1" }; + result = dataWriter.Write(instance1); + Assert.AreEqual(ReturnCode.Ok, result); + + var duration = new Duration { Seconds = 5 }; + result = dataWriter.WaitForAcknowledgments(duration); + Assert.AreEqual(ReturnCode.Ok, result); + + Assert.IsTrue(evtAlive.Wait(2_500)); + evtAlive.Reset(); + + result = dataWriter.Dispose(instance1); + Assert.AreEqual(ReturnCode.Ok, result); + + duration = new Duration { Seconds = 5 }; + result = dataWriter.WaitForAcknowledgments(duration); + Assert.AreEqual(ReturnCode.Ok, result); + + Assert.IsTrue(evtDisposed.Wait(2_500)); + Assert.AreEqual(1, countDisposed); + evtDisposed.Reset(); + + // Call dispose with the handle parameter + var instance2 = new TestInclude { Id = "2" }; + var handle2 = dataWriter.RegisterInstance(instance2); + Assert.AreNotEqual(InstanceHandle.HandleNil, handle2); + + result = dataWriter.Write(instance2, handle2); + Assert.AreEqual(ReturnCode.Ok, result); + + duration = new Duration { Seconds = 5 }; + result = dataWriter.WaitForAcknowledgments(duration); + Assert.AreEqual(ReturnCode.Ok, result); + + Assert.IsTrue(evtAlive.Wait(2_500)); + evtAlive.Reset(); + + result = dataWriter.Dispose(instance2, handle2); + Assert.AreEqual(ReturnCode.Ok, result); + + duration = new Duration { Seconds = 5 }; + result = dataWriter.WaitForAcknowledgments(duration); + Assert.AreEqual(ReturnCode.Ok, result); + + Assert.IsTrue(evtDisposed.Wait(2_500)); + Assert.AreEqual(2, countDisposed); + evtDisposed.Reset(); + + // Call dispose with the handle parameter and specific timestamp + var now = DateTime.Now.ToTimestamp(); + var instance3 = new TestInclude { Id = "3" }; + var handle3 = dataWriter.RegisterInstance(instance3); + Assert.AreNotEqual(InstanceHandle.HandleNil, handle3); + + result = dataWriter.Write(instance3, handle3); + Assert.AreEqual(ReturnCode.Ok, result); + + duration = new Duration { Seconds = 5 }; + result = dataWriter.WaitForAcknowledgments(duration); + Assert.AreEqual(ReturnCode.Ok, result); + + Assert.IsTrue(evtAlive.Wait(2_500)); + Assert.AreEqual(2, countDisposed); + evtAlive.Reset(); + + result = dataWriter.Dispose(instance3, handle3, now); + Assert.AreEqual(ReturnCode.Ok, result); + + duration = new Duration { Seconds = 5 }; + result = dataWriter.WaitForAcknowledgments(duration); + Assert.AreEqual(ReturnCode.Ok, result); + + Assert.IsTrue(evtDisposed.Wait(2_500)); + Assert.AreEqual(3, countDisposed); + Assert.AreEqual(now.Seconds, timestamp.Seconds); + Assert.AreEqual(now.NanoSeconds, timestamp.NanoSeconds); + + evtDisposed.Dispose(); + evtAlive.Dispose(); + + Assert.AreEqual(ReturnCode.Ok, dataReader.DeleteContainedEntities()); + Assert.AreEqual(ReturnCode.Ok, subscriber.DeleteDataReader(dataReader)); + Assert.AreEqual(ReturnCode.Ok, subscriber.DeleteContainedEntities()); + Assert.AreEqual(ReturnCode.Ok, _publisher.DeleteDataWriter(writer)); + Assert.AreEqual(ReturnCode.Ok, _participant.DeleteSubscriber(subscriber)); + } + + /// + /// Test the method. + /// + [TestMethod] + [TestCategory(TEST_CATEGORY)] + public void TestGetKeyValue() + { + // Initialize entities + var writer = _publisher.CreateDataWriter(_topic); + Assert.IsNotNull(writer); + var dataWriter = new TestIncludeDataWriter(writer); + + // Call GetKeyValue with HandleNil + var data = new TestInclude(); + var result = dataWriter.GetKeyValue(data, InstanceHandle.HandleNil); + Assert.AreEqual(ReturnCode.BadParameter, result); + + // Register an instance + var instance = new TestInclude { Id = "1" }; + var handle = dataWriter.RegisterInstance(instance); + Assert.AreNotEqual(InstanceHandle.HandleNil, handle); + + // Call GetKeyValue + data = new TestInclude(); + result = dataWriter.GetKeyValue(data, handle); + Assert.AreEqual(ReturnCode.Ok, result); + Assert.AreEqual("1", data.Id); + + _publisher.DeleteDataWriter(writer); + } + + /// + /// Test the method. + /// + [TestMethod] + [TestCategory(TEST_CATEGORY)] + public void TestLookupInstance() + { + // Initialize entities + var writer = _publisher.CreateDataWriter(_topic); + Assert.IsNotNull(writer); + var dataWriter = new TestIncludeDataWriter(writer); + + // Lookup for a non-existing instance + var handle = dataWriter.LookupInstance(new TestInclude { Id = "1" }); + Assert.AreEqual(InstanceHandle.HandleNil, handle); + + // Register an instance + var handle1 = dataWriter.RegisterInstance(new TestInclude { Id = "1" }); + Assert.AreNotEqual(InstanceHandle.HandleNil, handle1); + + // Lookup for an existing instance + handle = dataWriter.LookupInstance(new TestInclude { Id = "1" }); + Assert.AreNotEqual(InstanceHandle.HandleNil, handle); + Assert.AreEqual(handle1, handle); + + _publisher.DeleteDataWriter(writer); + } + #endregion + } +} diff --git a/Tests/OpenDDSharp.UnitTest/DataWriterTest.cs b/Tests/OpenDDSharp.UnitTest/DataWriterTest.cs index 583c0616..bc970348 100644 --- a/Tests/OpenDDSharp.UnitTest/DataWriterTest.cs +++ b/Tests/OpenDDSharp.UnitTest/DataWriterTest.cs @@ -793,7 +793,7 @@ public void TestGetMatchedSubscriptionData() // https://github.com/OpenDDS/OpenDDS/blob/master/docs/design/RTPS // OPENDDS ISSUE: GetMatchedSubscriptions returns local entities but GetMatchedSubscriptionData doesn't - // because is looking in the Built-in topic. If not found in the built-in, shouldn't try to look locally? + // because it is looking in the Built-in topic. If not found in the built-in, shouldn't try to look locally? // WORKAROUND: Create another participant for the DataReader. var otherParticipant = AssemblyInitializer.Factory.CreateParticipant(AssemblyInitializer.RTPS_DOMAIN); Assert.IsNotNull(otherParticipant); diff --git a/Tests/OpenDDSharp.UnitTest/TransportRegistryTest.cs b/Tests/OpenDDSharp.UnitTest/TransportRegistryTest.cs index d9ea3028..5bbe68c8 100644 --- a/Tests/OpenDDSharp.UnitTest/TransportRegistryTest.cs +++ b/Tests/OpenDDSharp.UnitTest/TransportRegistryTest.cs @@ -124,7 +124,7 @@ public void TestCreateInst() Assert.IsNotNull(shmemInst); TransportRegistry.Instance.RemoveInst(inst); - // Create a instance with an invalid transport type + // Create an instance with an invalid transport type inst = TransportRegistry.Instance.CreateInst(nameof(TestCreateInst), "quantic_teletransportation"); Assert.IsNull(inst); @@ -173,11 +173,11 @@ public void TestCreateInst() [TestCategory(TEST_CATEGORY)] public void TestGetInst() { - // Get a not existing transport instance + // Get not existing transport instance var inst = TransportRegistry.Instance.GetInst(nameof(TestGetInst)); Assert.IsNull(inst); - // Create a new transport instance an try to get it + // Create a new transport instance and try to get it var created = TransportRegistry.Instance.CreateInst(nameof(TestGetInst), "shmem"); Assert.IsNotNull(created); @@ -189,7 +189,7 @@ public void TestGetInst() inst = TransportRegistry.Instance.GetInst(null); Assert.IsNull(inst); - inst = TransportRegistry.Instance.GetInst(""); + inst = TransportRegistry.Instance.GetInst(string.Empty); Assert.IsNull(inst); inst = TransportRegistry.Instance.GetInst(" "); @@ -295,7 +295,7 @@ public void TestCreateConfig() [TestCategory(TEST_CATEGORY)] public void TestGetConfig() { - // Get a not existing transport config + // Get not existing transport config var config = TransportRegistry.Instance.GetConfig(nameof(TestGetConfig)); Assert.IsNull(config); @@ -304,7 +304,7 @@ public void TestGetConfig() Assert.IsNotNull(config); Assert.AreEqual("_OPENDDS_DEFAULT_CONFIG", config.Name); - // Create a new transport config an try to get it + // Create a new transport config and try to get it var created = TransportRegistry.Instance.CreateConfig(nameof(TestGetConfig)); Assert.IsNotNull(created); diff --git a/Tests/TestIdlCdr/TestIdlCdr.csproj b/Tests/TestIdlCdr/TestIdlCdr.csproj index ca90e7c4..8e8eac7f 100644 --- a/Tests/TestIdlCdr/TestIdlCdr.csproj +++ b/Tests/TestIdlCdr/TestIdlCdr.csproj @@ -16,6 +16,7 @@ true win-x64;win-x86;linux-x64;osx-x64;osx-arm64; TestIdlCdr + false true From e13ece0c19a255d56b110527f383b0e7619c2b09 Mon Sep 17 00:00:00 2001 From: jose_morato Date: Wed, 23 Oct 2024 15:46:47 +0200 Subject: [PATCH 2/2] Serialized Time_t --- Native/CSharpCDRImplTemplate.txt | 40 ++++++++++++++++++------------- Native/CWrapperHeaderTemplate.txt | 8 +++---- Native/CWrapperImplTemplate.txt | 12 ++++++---- Native/marshal.h | 20 +++++++++++++++- Sources/OpenDDSharp/Timestamp.cs | 4 ++-- 5 files changed, 57 insertions(+), 27 deletions(-) diff --git a/Native/CSharpCDRImplTemplate.txt b/Native/CSharpCDRImplTemplate.txt index 6e8f8fca..42d9ba92 100644 --- a/Native/CSharpCDRImplTemplate.txt +++ b/Native/CSharpCDRImplTemplate.txt @@ -150,8 +150,9 @@ InstanceHandle ret = InstanceHandle.HandleNil; var bytes = _typeSupport.EncodeToBytes(instance); + var tsBytes = timestamp.ToCDR().ToArray(); - ret = <%TYPE%>DataWriterNative.RegisterInstanceTimestamp(_native, bytes, (UIntPtr)bytes.Length, timestamp); + ret = <%TYPE%>DataWriterNative.RegisterInstanceTimestamp(_native, bytes, (UIntPtr)bytes.Length, tsBytes, (UIntPtr)tsBytes.Length); return ret; } @@ -179,8 +180,9 @@ public ReturnCode UnregisterInstance(<%TYPE%> data, InstanceHandle handle, Timestamp timestamp) { var bytes = _typeSupport.EncodeToBytes(data); + var tsBytes = timestamp.ToCDR().ToArray(); - return (ReturnCode)<%TYPE%>DataWriterNative.UnregisterInstanceTimestamp(_native, bytes, (UIntPtr)bytes.Length, handle, timestamp); + return (ReturnCode)<%TYPE%>DataWriterNative.UnregisterInstanceTimestamp(_native, bytes, (UIntPtr)bytes.Length, handle, tsBytes, (UIntPtr)tsBytes.Length); } public ReturnCode Write(<%TYPE%> data) @@ -214,8 +216,9 @@ ReturnCode ret = ReturnCode.Error; var bytes = _typeSupport.EncodeToBytes(data); + var tsBytes = timestamp.ToCDR().ToArray(); - ret = (ReturnCode)<%TYPE%>DataWriterNative.WriteWithTimestamp(_native, bytes, (UIntPtr)bytes.Length, handle, timestamp); + ret = (ReturnCode)<%TYPE%>DataWriterNative.WriteWithTimestamp(_native, bytes, (UIntPtr)bytes.Length, handle, tsBytes, (UIntPtr)tsBytes.Length); return ret; } @@ -251,8 +254,9 @@ ReturnCode ret = ReturnCode.Error; var bytes = _typeSupport.EncodeToBytes(data); + var tsBytes = timestamp.ToCDR().ToArray(); - ret = (ReturnCode)<%TYPE%>DataWriterNative.DisposeTimestamp(_native, bytes, (UIntPtr)bytes.Length, handle, timestamp); + ret = (ReturnCode)<%TYPE%>DataWriterNative.DisposeTimestamp(_native, bytes, (UIntPtr)bytes.Length, handle, tsBytes, (UIntPtr)tsBytes.Length); return ret; } @@ -315,8 +319,9 @@ internal static partial int Write(IntPtr dw, byte[] cdrData, UIntPtr size, int handle); [SuppressUnmanagedCodeSecurity] - [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_WriteWithTimestamp_Cdr", CallingConvention = CallingConvention.Cdecl)] - internal static extern int WriteWithTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, int handle, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); + [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_WriteWithTimestamp_Cdr")] + [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvSuppressGCTransition) })] + internal static partial int WriteWithTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, int handle, byte[] tsCdr, UIntPtr tsSize); [SuppressUnmanagedCodeSecurity] [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_RegisterInstance_Cdr")] @@ -324,8 +329,9 @@ internal static partial int RegisterInstance(IntPtr dw, byte[] cdrData, UIntPtr size); [SuppressUnmanagedCodeSecurity] - [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_RegisterInstanceTimestamp_Cdr", CallingConvention = CallingConvention.Cdecl)] - internal static extern int RegisterInstanceTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); + [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_RegisterInstanceTimestamp_Cdr")] + [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvSuppressGCTransition) })] + internal static partial int RegisterInstanceTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, byte[] tsCdr, UIntPtr tsSize); [SuppressUnmanagedCodeSecurity] [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_UnregisterInstance_Cdr")] @@ -333,8 +339,9 @@ internal static partial int UnregisterInstance(IntPtr dw, byte[] cdrData, UIntPtr size, int handle); [SuppressUnmanagedCodeSecurity] - [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_UnregisterInstanceTimestamp_Cdr", CallingConvention = CallingConvention.Cdecl)] - internal static extern int UnregisterInstanceTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, int handle, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); + [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_UnregisterInstanceTimestamp_Cdr")] + [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvSuppressGCTransition) })] + internal static partial int UnregisterInstanceTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, int handle, byte[] tsCdr, UIntPtr tsSize); [SuppressUnmanagedCodeSecurity] [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_LookupInstance_Cdr")] @@ -347,8 +354,9 @@ internal static partial int Dispose(IntPtr dw, byte[] cdrData, UIntPtr size, int handle); [SuppressUnmanagedCodeSecurity] - [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_DisposeTimestamp_Cdr", CallingConvention = CallingConvention.Cdecl)] - internal static extern int DisposeTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, int handle, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); + [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_DisposeTimestamp_Cdr")] + [UnmanagedCallConv(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvSuppressGCTransition) })] + internal static partial int DisposeTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, int handle, byte[] tsCdr, UIntPtr tsSize); [SuppressUnmanagedCodeSecurity] [LibraryImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_GetKeyValue_Cdr")] @@ -365,7 +373,7 @@ [SuppressUnmanagedCodeSecurity] [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_WriteWithTimestamp_Cdr", CallingConvention = CallingConvention.Cdecl)] - internal static extern int WriteWithTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, int handle, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); + internal static extern int WriteWithTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, int handle, byte[] tsCdr, UIntPtr tsSize); [SuppressUnmanagedCodeSecurity] [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_RegisterInstance_Cdr", CallingConvention = CallingConvention.Cdecl)] @@ -373,7 +381,7 @@ [SuppressUnmanagedCodeSecurity] [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_RegisterInstanceTimestamp_Cdr", CallingConvention = CallingConvention.Cdecl)] - internal static extern int RegisterInstanceTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); + internal static extern int RegisterInstanceTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, byte[] tsCdr, UIntPtr tsSize); [SuppressUnmanagedCodeSecurity] [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_UnregisterInstance_Cdr", CallingConvention = CallingConvention.Cdecl)] @@ -381,7 +389,7 @@ [SuppressUnmanagedCodeSecurity] [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_UnregisterInstanceTimestamp_Cdr", CallingConvention = CallingConvention.Cdecl)] - internal static extern int UnregisterInstanceTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, int handle, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); + internal static extern int UnregisterInstanceTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, int handle, byte[] tsCdr, UIntPtr tsSize); [SuppressUnmanagedCodeSecurity] [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_LookupInstance_Cdr", CallingConvention = CallingConvention.Cdecl)] @@ -393,7 +401,7 @@ [SuppressUnmanagedCodeSecurity] [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_DisposeTimestamp_Cdr", CallingConvention = CallingConvention.Cdecl)] - internal static extern int DisposeTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, int handle, [MarshalAs(UnmanagedType.Struct), In] Timestamp timestamp); + internal static extern int DisposeTimestamp(IntPtr dw, byte[] cdrData, UIntPtr size, int handle, byte[] tsCdr, UIntPtr tsSize); [SuppressUnmanagedCodeSecurity] [DllImport(<%TYPE%>.API_DLL, EntryPoint = "<%SCOPED_METHOD%>DataWriter_GetKeyValue_Cdr", CallingConvention = CallingConvention.Cdecl)] diff --git a/Native/CWrapperHeaderTemplate.txt b/Native/CWrapperHeaderTemplate.txt index 30ff9f19..84f32f76 100644 --- a/Native/CWrapperHeaderTemplate.txt +++ b/Native/CWrapperHeaderTemplate.txt @@ -21,7 +21,7 @@ EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_Write_Cdr(<%SCOPED%>DataWri EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_WriteWithTimestamp_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data, int handle, ::DDS::Time_t time); -EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_WriteWithTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, int handle, ::DDS::Time_t time); +EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_WriteWithTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, int handle, const char* time_data, size_t time_size); EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_RegisterInstance_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data); @@ -29,7 +29,7 @@ EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_RegisterInstance_Cdr(<%SCOP EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_RegisterInstanceTimestamp_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data, ::DDS::Time_t time); -EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_RegisterInstanceTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, ::DDS::Time_t time); +EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_RegisterInstanceTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, const char* time_data, size_t time_size); EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_LookupInstance_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data); @@ -41,7 +41,7 @@ EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_UnregisterInstance_Cdr(<%SC EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_UnregisterInstanceTimestamp_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data, ::DDS::InstanceHandle_t handle, ::DDS::Time_t time); -EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_UnregisterInstanceTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, ::DDS::InstanceHandle_t handle, ::DDS::Time_t time); +EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_UnregisterInstanceTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, ::DDS::InstanceHandle_t handle, const char* time_data, size_t time_size); EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_Dispose_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data, int handle); @@ -49,7 +49,7 @@ EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_Dispose_Cdr(<%SCOPED%>DataW EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_DisposeTimestamp_Json(<%SCOPED%>DataWriter_ptr dw, const char* json_data, int handle, ::DDS::Time_t time); -EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_DisposeTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, int handle, ::DDS::Time_t time); +EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_DisposeTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, int handle, const char* time_data, size_t time_size); EXTERN_METHOD_EXPORT int <%SCOPED_METHOD%>DataWriter_GetKeyValue_Json(<%SCOPED%>DataWriter_ptr dw, char* & json_data, int handle); diff --git a/Native/CWrapperImplTemplate.txt b/Native/CWrapperImplTemplate.txt index aa55e528..158e2677 100644 --- a/Native/CWrapperImplTemplate.txt +++ b/Native/CWrapperImplTemplate.txt @@ -59,9 +59,10 @@ int <%SCOPED_METHOD%>DataWriter_WriteWithTimestamp_Json(<%SCOPED%>DataWriter_ptr return dw->write_w_timestamp(sample, handle, time); } -int <%SCOPED_METHOD%>DataWriter_WriteWithTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, int handle, ::DDS::Time_t time) +int <%SCOPED_METHOD%>DataWriter_WriteWithTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, int handle, const char* time_data, size_t time_size) { <%SCOPED%> sample = <%SCOPED_METHOD%>_deserialize_from_bytes(cdr_data, size); + ::DDS::Time_t time = marshal::dds_time_deserialize_from_bytes(time_data, time_size); return dw->write_w_timestamp(sample, handle, time); } @@ -97,9 +98,10 @@ int <%SCOPED_METHOD%>DataWriter_RegisterInstanceTimestamp_Json(<%SCOPED%>DataWri return dw->register_instance_w_timestamp(sample, time); } -int <%SCOPED_METHOD%>DataWriter_RegisterInstanceTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, ::DDS::Time_t time) +int <%SCOPED_METHOD%>DataWriter_RegisterInstanceTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, const char* time_data, size_t time_size) { <%SCOPED%> sample = <%SCOPED_METHOD%>_deserialize_from_bytes(cdr_data, size); + ::DDS::Time_t time = marshal::dds_time_deserialize_from_bytes(time_data, time_size); return dw->register_instance_w_timestamp(sample, time); } @@ -135,9 +137,10 @@ int <%SCOPED_METHOD%>DataWriter_UnregisterInstanceTimestamp_Json(<%SCOPED%>DataW return dw->unregister_instance_w_timestamp(sample, handle, time); } -int <%SCOPED_METHOD%>DataWriter_UnregisterInstanceTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, ::DDS::InstanceHandle_t handle, ::DDS::Time_t time) +int <%SCOPED_METHOD%>DataWriter_UnregisterInstanceTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, ::DDS::InstanceHandle_t handle, const char* time_data, size_t time_size) { <%SCOPED%> sample = <%SCOPED_METHOD%>_deserialize_from_bytes(cdr_data, size); + ::DDS::Time_t time = marshal::dds_time_deserialize_from_bytes(time_data, time_size); return dw->unregister_instance_w_timestamp(sample, handle, time); } @@ -192,9 +195,10 @@ int <%SCOPED_METHOD%>DataWriter_DisposeTimestamp_Json(<%SCOPED%>DataWriter_ptr d return dw->dispose_w_timestamp(sample, handle, time); } -int <%SCOPED_METHOD%>DataWriter_DisposeTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, int handle, ::DDS::Time_t time) +int <%SCOPED_METHOD%>DataWriter_DisposeTimestamp_Cdr(<%SCOPED%>DataWriter_ptr dw, const char* cdr_data, size_t size, int handle, const char* time_data, size_t time_size) { <%SCOPED%> sample = <%SCOPED_METHOD%>_deserialize_from_bytes(cdr_data, size); + ::DDS::Time_t time = marshal::dds_time_deserialize_from_bytes(time_data, time_size); return dw->dispose_w_timestamp(sample, handle, time); } diff --git a/Native/marshal.h b/Native/marshal.h index a97191dc..a05c0e8f 100644 --- a/Native/marshal.h +++ b/Native/marshal.h @@ -3,6 +3,8 @@ #include "ace/Basic_Types.h" #include "tao/Unbounded_Value_Sequence_T.h" +#include "dds/DCPS/Serializer.h" +#include "dds/DdsDcpsCoreC.h" class marshal { @@ -498,7 +500,7 @@ class marshal { static void *wchar_to_ptr(wchar_t wchar) { const size_t size = sizeof(wchar_t); - // Alloc memory for the poninter + // Alloc memory for the pointer void *ptr = ACE_OS::malloc(size); // Copy the bytes in the pointer @@ -506,6 +508,22 @@ class marshal { return ptr; } + + static DDS::Time_t dds_time_deserialize_from_bytes(const char *bytes, size_t size) { + const OpenDDS::DCPS::Encoding encoding(OpenDDS::DCPS::Encoding::KIND_XCDR1, OpenDDS::DCPS::ENDIAN_LITTLE); + ACE_Message_Block mb(size); + mb.copy(bytes, size); + OpenDDS::DCPS::Serializer serializer(&mb, encoding); + DDS::Time_t time_value; + if (!(serializer >> time_value.sec)) { + throw std::runtime_error("Failed to deserialize DDS::Time_t seconds from bytes"); + } + + if (!(serializer >> time_value.nanosec)) { + throw std::runtime_error("Failed to deserialize DDS::Time_t nanoseconds from bytes"); + } + return time_value; + } }; #endif \ No newline at end of file diff --git a/Sources/OpenDDSharp/Timestamp.cs b/Sources/OpenDDSharp/Timestamp.cs index 97e3220f..fcca90b4 100644 --- a/Sources/OpenDDSharp/Timestamp.cs +++ b/Sources/OpenDDSharp/Timestamp.cs @@ -58,7 +58,7 @@ public uint NanoSeconds /// Converts the time value to a CDR representation. /// /// The byte span serialized. - internal ReadOnlySpan ToCDR() + public ReadOnlySpan ToCDR() { var writer = new Marshaller.Cdr.CdrWriter(); writer.WriteInt32(Seconds); @@ -70,7 +70,7 @@ internal ReadOnlySpan ToCDR() /// Updates the time value from a CDR representation. /// /// The byte span serialized. - internal void FromCDR(ReadOnlySpan data) + public void FromCDR(ReadOnlySpan data) { var reader = new Marshaller.Cdr.CdrReader(data.ToArray()); Seconds = reader.ReadInt32();