Skip to content

Commit

Permalink
Add detailed explanation on service handler invocation.
Browse files Browse the repository at this point in the history
  • Loading branch information
noelex committed Mar 1, 2023
1 parent 268df90 commit 438d314
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 7 deletions.
1 change: 1 addition & 0 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Product>rclnet</Product>
<VersionPrefix>1.0.1</VersionPrefix>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
Expand Down
2 changes: 2 additions & 0 deletions src/Rcl.NET.Tests/NameGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ internal class NameGenerator
public static string GenerateNodeName() => $"test_node_{Interlocked.Increment(ref _id)}";

public static string GenerateTopicName() => $"test_topic_{Interlocked.Increment(ref _id)}";

public static string GenerateServiceName() => $"test_service_{Interlocked.Increment(ref _id)}";
}
49 changes: 49 additions & 0 deletions src/Rcl.NET.Tests/ServiceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Rosidl.Messages.Rcl;

namespace Rcl.NET.Tests;

public class ServiceTests
{
[Fact]
public async Task ClientRequestTimeout()
{
using var context = new RclContext(TestConfig.DefaultContextArguments);
using var node = context.CreateNode(NameGenerator.GenerateNodeName());
using var client = node.CreateClient<
ListParametersService,
ListParametersServiceRequest,
ListParametersServiceResponse>(NameGenerator.GenerateServiceName());

await Assert.ThrowsAsync<TimeoutException>(async () =>
await client.InvokeAsync(new ListParametersServiceRequest(), 100));
}

[Fact]
public async Task NormalServiceCall()
{
using var context = new RclContext(TestConfig.DefaultContextArguments);
using var node = context.CreateNode(NameGenerator.GenerateNodeName());

var response = new ListParametersServiceResponse(new(new[] { "n1", "n2" }, new[] { "p1", "p2" }));
var service = NameGenerator.GenerateServiceName();

using var server = node.CreateService<
ListParametersService,
ListParametersServiceRequest,
ListParametersServiceResponse>(service, (request, state) =>
{
return response;
});

using var clientNode = context.CreateNode(NameGenerator.GenerateNodeName());
using var client = clientNode.CreateClient<
ListParametersService,
ListParametersServiceRequest,
ListParametersServiceResponse>(service);

var actualResponse = await client.InvokeAsync(new ListParametersServiceRequest());

Assert.True(response.Result.Names.SequenceEqual(actualResponse.Result.Names));
Assert.True(response.Result.Prefixes.SequenceEqual(actualResponse.Result.Prefixes));
}
}
30 changes: 27 additions & 3 deletions src/Rcl.NET/IRclService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,22 @@ public interface IRclService : IRclObject
/// <summary>
/// Represents a handler which is handles service requests on the <see cref="RclContext"/> event loop.
/// </summary>
/// <typeparam name="TRequest"></typeparam>
/// <typeparam name="TResponse"></typeparam>
/// <remarks>
/// The handler will be invoked on the <see cref="RclContext"/> event loop to process incoming requests.
/// Performing blocking operations in the handler will affect the responsiveness of the event loop.
/// If the handler issues blocking calls or performs CPU intensive calculations, it's recommended to
/// use <see cref="IConcurrentServiceHandler{TRequest, TResponse}"/> instead.
/// </remarks>
/// <typeparam name="TRequest">Type of the request messages.</typeparam>
/// <typeparam name="TResponse">Type of the response messages.</typeparam>
public interface IServiceHandler<TRequest, TResponse>
where TRequest : IServiceRequest
where TResponse : IServiceResponse
{
/// <summary>
/// Process the incoming request.
/// </summary>
/// <param name="request"></param>
/// <param name="request">The request message received from the client.</param>
/// <returns>The response message to be sent to the client.</returns>
TResponse ProcessRequest(TRequest request);
}
Expand All @@ -49,6 +55,12 @@ public interface IServiceHandler<TRequest, TResponse>
/// This is especially useful when dealing with complex messages or messages
/// has large array fields. Operaing directly on native buffers would effectively
/// lower the cost of copying and reduce managed heap allocation.
/// <para>
/// The handler will be invoked on the <see cref="RclContext"/> event loop to process incoming requests.
/// Performing blocking operations in the handler will affect the responsiveness of the event loop.
/// If the handler issues blocking calls or performs CPU intensive calculations, it's recommended to
/// use <see cref="IConcurrentNativeServiceHandler"/> instead.
/// </para>
/// </remarks>
public interface INativeServiceHandler
{
Expand All @@ -73,6 +85,12 @@ public interface INativeServiceHandler
/// <summary>
/// Represents a handler which is able to handle multiple service requests concurrently.
/// </summary>
/// <remarks>
/// The handler will start its execution on the <see cref="RclContext"/> event loop to process incoming requests.
/// If the handler issues blocking calls or performs CPU intensive calculations, <see cref="Task.Yield"/> should
/// be called before performing any blocking operation, or you can use <see cref="Task.Run(Action)"/> to perform the
/// blocking task in a background thread.
/// </remarks>
/// <typeparam name="TRequest">Type of the request messages.</typeparam>
/// <typeparam name="TResponse">Type of the response messages.</typeparam>
public interface IConcurrentServiceHandler<TRequest, TResponse>
Expand All @@ -91,6 +109,12 @@ public interface IConcurrentServiceHandler<TRequest, TResponse>
/// <summary>
/// Same as <see cref="INativeServiceHandler"/>, but handles requests concurrently.
/// </summary>
/// <remarks>
/// The handler will start its execution on the <see cref="RclContext"/> event loop to process incoming requests.
/// If the handler issues blocking calls or performs CPU intensive calculations, <see cref="Task.Yield"/> should
/// be called before performing any blocking operation, or you can use <see cref="Task.Run(Action)"/> to perform the
/// blocking task in a background thread.
/// </remarks>
public interface IConcurrentNativeServiceHandler
{
/// <summary>
Expand Down
1 change: 0 additions & 1 deletion src/Rcl.NET/Rcl.NET.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
</PropertyGroup>

<PropertyGroup>
<VersionPrefix>1.0.0</VersionPrefix>
<PackageId>Rcl.NET</PackageId>
<Description>
<![CDATA[rclnet is a fast and easy-to-use .NET wrapper over ROS 2 client library, allowing .NET applications to interact with other ROS applications.]]>
Expand Down
4 changes: 2 additions & 2 deletions src/Rcl.NET/RclClock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ private RclClock(RclClockType type, bool shared)
/// Get an <see cref="RclClock"/> representing an <see cref="RclClockType.Steady"/> clock.
/// </summary>
/// <remarks>
/// This is a shared clock, diposing it will cause an <see cref="InvalidOperationException"/>.
/// This is a shared clock, disposing it will cause an <see cref="InvalidOperationException"/>.
/// </remarks>
public static RclClock Steady { get; } = new RclClock(RclClockType.Steady, true);

/// <summary>
/// Get an <see cref="RclClock"/> representing an <see cref="RclClockType.System"/> clock.
/// </summary>
/// <remarks>
/// This is a shared clock, diposing it will cause an <see cref="InvalidOperationException"/>.
/// This is a shared clock, disposing it will cause an <see cref="InvalidOperationException"/>.
/// </remarks>
public static RclClock System { get; } = new RclClock(RclClockType.System, true);

Expand Down
1 change: 0 additions & 1 deletion src/Rosidl.Runtime/Rosidl.Runtime.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
</PropertyGroup>

<PropertyGroup>
<VersionPrefix>1.0.0</VersionPrefix>
<PackageId>Rosidl.Runtime</PackageId>
<Description>
<![CDATA[Provide runtime support for message classes / structs generated by ros2cs utility.]]>
Expand Down

0 comments on commit 438d314

Please sign in to comment.