Skip to content

Commit

Permalink
actions: extend idl generation to include interfaces for "action wrap…
Browse files Browse the repository at this point in the history
…per types"

With "action wrapper types" this means the ROS messages and services that add the `GoalId`, `TimeStamp` or other fields on top of the user defined types for the action.
The introduced methods in `IRosActionDefinition` are needed to work around the missing existential type support in C# (dotnet/csharplang#5556).
  • Loading branch information
hoffmann-stefan committed May 3, 2023
1 parent 48c7bf3 commit 9e85e23
Show file tree
Hide file tree
Showing 12 changed files with 288 additions and 16 deletions.
5 changes: 5 additions & 0 deletions rcldotnet_common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ find_package(DotNETExtra REQUIRED)
set(CS_SOURCES
DllLoadUtils.cs
IRosActionDefinition.cs
IRosActionFeedbackMessage.cs
IRosActionGetResultRequest.cs
IRosActionGetResultResponse.cs
IRosActionSendGoalRequest.cs
IRosActionSendGoalResponse.cs
IRosMessage.cs
IRosServiceDefinition.cs
)
Expand Down
15 changes: 15 additions & 0 deletions rcldotnet_common/IRosActionDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,20 @@ public interface IRosActionDefinition<TGoal, TResult, TFeedback>
// must be implemented on deriving types, gets called via reflection
// (static abstract interface members are not supported yet.)
// public static abstract IntPtr __GetTypeSupport();

// public static abstract IRosActionSendGoalRequest<TGoal> __CreateSendGoalRequest();
// public static abstract SafeHandle __CreateSendGoalRequestHandle();

// public static abstract IRosActionSendGoalResponse __CreateSendGoalResponse();
// public static abstract SafeHandle __CreateSendGoalResponseHandle();

// public static abstract IRosActionGetResultRequest __CreateGetResultRequest();
// public static abstract SafeHandle __CreateGetResultRequestHandle();

// public static abstract IRosActionGetResultResponse<TResult> __CreateGetResultResponse();
// public static abstract SafeHandle __CreateGetResultResponseHandle();

// public static abstract IRosActionFeedbackMessage<TFeedback> __CreateFeedbackMessage();
// public static abstract SafeHandle __CreateFeedbackMessageHandle();
}
}
26 changes: 26 additions & 0 deletions rcldotnet_common/IRosActionFeedbackMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* Copyright 2022 Stefan Hoffmann <stefan.hoffmann@schiller.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace ROS2
{
public interface IRosActionFeedbackMessage<TFeedback> : IRosMessage
where TFeedback : IRosMessage, new()
{
// NOTICE: cyclic reference, see `IRosActionSendGoalRequest<TGoal>`
// unique_identifier_msgs.msg.UUID GoalId { get; set; }

TFeedback Feedback { get; set; }
}
}
23 changes: 23 additions & 0 deletions rcldotnet_common/IRosActionGetResultRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* Copyright 2022 Stefan Hoffmann <stefan.hoffmann@schiller.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace ROS2
{
public interface IRosActionGetResultRequest : IRosMessage
{
// NOTICE: cyclic reference, see `IRosActionSendGoalRequest<TGoal>`
// unique_identifier_msgs.msg.UUID GoalId { get; set; }
}
}
25 changes: 25 additions & 0 deletions rcldotnet_common/IRosActionGetResultResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* Copyright 2022 Stefan Hoffmann <stefan.hoffmann@schiller.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace ROS2
{
public interface IRosActionGetResultResponse<TResult> : IRosMessage
where TResult : IRosMessage, new()
{
sbyte Status { get; set; }

TResult Result { get; set; }
}
}
42 changes: 42 additions & 0 deletions rcldotnet_common/IRosActionSendGoalRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* Copyright 2022 Stefan Hoffmann <stefan.hoffmann@schiller.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace ROS2
{
public interface IRosActionSendGoalRequest<TGoal> : IRosMessage
where TGoal : IRosMessage, new()
{
// NOTICE: This would cause a cyclic reference:
//
// - `unique_identifier_msgs.msg.UUID` in the `unique_identifier_msgs`
// assembly references `IRosMessage` in the `rcldotnet_common`
// assembly.
// - `IRosActionSendGoalRequest<TGoal>` in the `rcldotnet_common`
// assembly references `unique_identifier_msgs.msg.UUID` in the
// `unique_identifier_msgs` assembly.
//
// So we need a workaround:
// - Use reflection later on to get to this.
// - Or use types like `byte[]` (or ValueTuple<int, int> for
// `builtin_interfaces.msg.Time`) and generate accessor methods that
// convert and use those types.
// - Or provide property `IRosMessage GoalIdRosMessage { get; set; }`
// and cast to the concrete type on usage.
//
// unique_identifier_msgs.msg.UUID GoalId { get; set; }

TGoal Goal { get; set; }
}
}
25 changes: 25 additions & 0 deletions rcldotnet_common/IRosActionSendGoalResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* Copyright 2022 Stefan Hoffmann <stefan.hoffmann@schiller.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace ROS2
{
public interface IRosActionSendGoalResponse : IRosMessage
{
bool Accepted { get; set; }

// NOTICE: cyclic reference, see `IRosActionSendGoalRequest<TGoal>`
// builtin_interfaces.msg.Time Stamp { get; set; }
}
}
90 changes: 86 additions & 4 deletions rosidl_generator_dotnet/resource/action.cs.em
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@{
from rosidl_generator_dotnet import get_field_name
from rosidl_generator_dotnet import get_dotnet_type
from rosidl_generator_dotnet import get_dotnet_type_for_message
from rosidl_generator_dotnet import get_builtin_dotnet_type
from rosidl_generator_dotnet import constant_value_to_dotnet

Expand All @@ -14,9 +15,17 @@ from rosidl_parser.definition import BasicType
from rosidl_parser.definition import NamespacedType

type_name = action.namespaced_type.name
goal_type_name = action.goal.structure.namespaced_type.name
result_type_name = action.result.structure.namespaced_type.name
feedback_type_name = action.feedback.structure.namespaced_type.name

goal_type_name = get_dotnet_type_for_message(action.goal)
result_type_name = get_dotnet_type_for_message(action.result)
feedback_type_name = get_dotnet_type_for_message(action.feedback)

send_goal_request_type_name = get_dotnet_type_for_message(action.send_goal_service.request_message)
send_goal_response_type_name = get_dotnet_type_for_message(action.send_goal_service.response_message)
get_result_request_type_name = get_dotnet_type_for_message(action.get_result_service.request_message)
get_result_response_type_name = get_dotnet_type_for_message(action.get_result_service.response_message)
feedback_message_type_name = get_dotnet_type_for_message(action.feedback_message)

action_typename = '%s__%s' % ('__'.join(action.namespaced_type.namespaces), type_name)
}
namespace @('.'.join(action.namespaced_type.namespaces))
Expand All @@ -28,7 +37,10 @@ namespace @('.'.join(action.namespaced_type.namespaces))
@# static abstract interface members are currently in preview, so maybe we could use the feature in the future.
@# (if hey add support to derive from static only interfaces in static classes)
@# Another option is to not use generics for passing the typesupport, but lets try this until we hit some wall.
public sealed class @(type_name) : global::ROS2.IRosActionDefinition<@(goal_type_name), @(result_type_name), @(feedback_type_name)>
public sealed class @(type_name) : global::ROS2.IRosActionDefinition<
global::@(goal_type_name),
global::@(result_type_name),
global::@(feedback_type_name)>
{
private static readonly DllLoadUtils dllLoadUtils;

Expand Down Expand Up @@ -57,5 +69,75 @@ namespace @('.'.join(action.namespaced_type.namespaces))
public static IntPtr __GetTypeSupport() {
return native_get_typesupport();
}

@# This method gets called via reflection as static abstract interface members are not supported yet.
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public static global::ROS2.IRosActionSendGoalRequest<global::@(goal_type_name)> __CreateSendGoalRequest()
{
return new global::@(send_goal_request_type_name)();
}

@# This method gets called via reflection as static abstract interface members are not supported yet.
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public static SafeHandle __CreateSendGoalRequestHandle()
{
return global::@(send_goal_request_type_name).__CreateMessageHandle();
}

@# This method gets called via reflection as static abstract interface members are not supported yet.
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public static global::ROS2.IRosActionSendGoalResponse __CreateSendGoalResponse()
{
return new global::@(send_goal_response_type_name)();
}

@# This method gets called via reflection as static abstract interface members are not supported yet.
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public static SafeHandle __CreateSendGoalResponseHandle()
{
return global::@(send_goal_response_type_name).__CreateMessageHandle();
}

@# This method gets called via reflection as static abstract interface members are not supported yet.
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public static global::ROS2.IRosActionGetResultRequest __CreateGetResultRequest()
{
return new global::@(get_result_request_type_name)();
}

@# This method gets called via reflection as static abstract interface members are not supported yet.
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public static SafeHandle __CreateGetResultRequestHandle()
{
return global::@(get_result_request_type_name).__CreateMessageHandle();
}

@# This method gets called via reflection as static abstract interface members are not supported yet.
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public static global::ROS2.IRosActionGetResultResponse<global::@(result_type_name)> __CreateGetResultResponse()
{
return new global::@(get_result_response_type_name)();
}

@# This method gets called via reflection as static abstract interface members are not supported yet.
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public static SafeHandle __CreateGetResultResponseHandle()
{
return global::@(get_result_response_type_name).__CreateMessageHandle();
}

@# This method gets called via reflection as static abstract interface members are not supported yet.
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public static global::ROS2.IRosActionFeedbackMessage<global::@(feedback_type_name)> __CreateFeedbackMessage()
{
return new global::@(feedback_message_type_name)();
}

@# This method gets called via reflection as static abstract interface members are not supported yet.
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public static SafeHandle __CreateFeedbackMessageHandle()
{
return global::@(feedback_message_type_name).__CreateMessageHandle();
}
}
}
23 changes: 18 additions & 5 deletions rosidl_generator_dotnet/resource/idl.cs.em
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// generated from rosidl_generator_dotnet/resource/idl.cs.em
// with input from @(package_name):@(interface_path)
// generated code does not contain a copyright notice
@{
from rosidl_generator_dotnet import get_dotnet_type_for_message
}@
@
@#######################################################################
@# EmPy template for generating <idl>.cs files
Expand Down Expand Up @@ -87,13 +90,17 @@ TEMPLATE(
@{
TEMPLATE(
'msg.cs.em',
package_name=package_name, interface_path=interface_path, message=action.send_goal_service.request_message)
package_name=package_name, interface_path=interface_path, message=action.send_goal_service.request_message,
action_interface='global::ROS2.IRosActionSendGoalRequest<global::%s>' % get_dotnet_type_for_message(action.goal)
)
}@

@{
TEMPLATE(
'msg.cs.em',
package_name=package_name, interface_path=interface_path, message=action.send_goal_service.response_message)
package_name=package_name, interface_path=interface_path, message=action.send_goal_service.response_message,
action_interface='global::ROS2.IRosActionSendGoalResponse'
)
}@

@{
Expand All @@ -105,13 +112,17 @@ TEMPLATE(
@{
TEMPLATE(
'msg.cs.em',
package_name=package_name, interface_path=interface_path, message=action.get_result_service.request_message)
package_name=package_name, interface_path=interface_path, message=action.get_result_service.request_message,
action_interface='global::ROS2.IRosActionGetResultRequest'
)
}@

@{
TEMPLATE(
'msg.cs.em',
package_name=package_name, interface_path=interface_path, message=action.get_result_service.response_message)
package_name=package_name, interface_path=interface_path, message=action.get_result_service.response_message,
action_interface='global::ROS2.IRosActionGetResultResponse<global::%s>' % get_dotnet_type_for_message(action.result)
)
}@

@{
Expand All @@ -123,7 +134,9 @@ TEMPLATE(
@{
TEMPLATE(
'msg.cs.em',
package_name=package_name, interface_path=interface_path, message=action.feedback_message)
package_name=package_name, interface_path=interface_path, message=action.feedback_message,
action_interface='global::ROS2.IRosActionFeedbackMessage<global::%s>' % get_dotnet_type_for_message(action.feedback)
)
}@

@{
Expand Down
Loading

0 comments on commit 9e85e23

Please sign in to comment.