rclrb 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/COPYING +373 -0
- data/README.md +13 -0
- data/lib/rclrb/callback_group.rb +31 -0
- data/lib/rclrb/capi.rb +369 -0
- data/lib/rclrb/client.rb +79 -0
- data/lib/rclrb/clock.rb +17 -0
- data/lib/rclrb/common.rb +43 -0
- data/lib/rclrb/executor.rb +34 -0
- data/lib/rclrb/fields.rb +224 -0
- data/lib/rclrb/future.rb +82 -0
- data/lib/rclrb/guard_condition.rb +18 -0
- data/lib/rclrb/init.rb +59 -0
- data/lib/rclrb/interfaces.rb +270 -0
- data/lib/rclrb/internal.rb +16 -0
- data/lib/rclrb/node.rb +143 -0
- data/lib/rclrb/publisher.rb +40 -0
- data/lib/rclrb/qos.rb +116 -0
- data/lib/rclrb/service.rb +46 -0
- data/lib/rclrb/subscription.rb +45 -0
- data/lib/rclrb/time.rb +32 -0
- data/lib/rclrb/timer.rb +31 -0
- data/lib/rclrb/version.rb +3 -0
- data/lib/rclrb/wait_set.rb +62 -0
- data/lib/rclrb.rb +43 -0
- metadata +98 -0
data/lib/rclrb/node.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
module Rclrb
|
2
|
+
##
|
3
|
+
# The node class is the center piece of RCL, it allows to create subscriptions, publishers timers,
|
4
|
+
# and services server and clients.
|
5
|
+
class Node
|
6
|
+
attr_reader :subscriptions, :timers, :services, :default_callback_group
|
7
|
+
##
|
8
|
+
# Create a node with the given +name+,
|
9
|
+
def initialize(name)
|
10
|
+
@node_handle = CApi::RclNodeT.new()
|
11
|
+
@services = []
|
12
|
+
@subscriptions = []
|
13
|
+
@timers = []
|
14
|
+
options = CApi.rcl_node_get_default_options()
|
15
|
+
@default_callback_group = MutuallyExclusiveCallbackGroup.new
|
16
|
+
CApi.handle_result(CApi.rcl_node_init(@node_handle, name, "", Rclrb.rcl_context(), options))
|
17
|
+
end
|
18
|
+
|
19
|
+
rclrb_finalize_with :@node_handle do |node_handle|
|
20
|
+
CApi.handle_result CApi.rcl_node_fini node_handle
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Get the namespace for this node
|
25
|
+
def get_namespace
|
26
|
+
return CApi.rcl_node_get_namespace @node_handle
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Create a new Publisher and return it.
|
31
|
+
#
|
32
|
+
# Parameters:
|
33
|
+
# * +msg_type+ The type of ROS messages the publisher will publish.
|
34
|
+
# * +topic+ (string) The name of the topic the publisher will publish to.
|
35
|
+
# * +qos_profile+ (QoSProfile or int) A QoSProfile or a history depth to apply to the publisher. In the case that a history depth is provided, the QoS history is set to RMW_QOS_POLICY_HISTORY_KEEP_LAST, the QoS history depth is set to the value of the parameter, and all other QoS settings are set to their default values.
|
36
|
+
# * +callback_group+ (optional CallbackGroup) The callback group for the publisher’s event handlers. If None, then the node’s default callback group is used.
|
37
|
+
def create_publisher(msg_type, topic, qos_profile, callback_group=nil, event_callbacks=nil)
|
38
|
+
callback_group = @default_callback_group if callback_group.nil?
|
39
|
+
## * +event_callbacks+ (Optional) – User-defined callbacks for middleware events.
|
40
|
+
if event_callbacks != nil
|
41
|
+
raise RclError.new("event_callbacks are not supported yet")
|
42
|
+
end
|
43
|
+
handle = CApi::rcl_get_zero_initialized_publisher()
|
44
|
+
publisher = Publisher.new(handle, @node_handle, msg_type, topic, qos_profile, callback_group, event_callbacks)
|
45
|
+
return publisher
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Create a new Subscription and return it. This function will also add the Subscription object to @subscriptions.
|
50
|
+
# To unsubscribe it is necesserary to call tje unsubscribe function with the handle to the subscriber.
|
51
|
+
#
|
52
|
+
# Parameters:
|
53
|
+
# * +msg_type+ The type of ROS messages the subscription will subscribe to.
|
54
|
+
# * +topic+ (string) – The name of the topic the subscription will subscribe to.
|
55
|
+
# * +callback+ (block) – A user-defined callback function that is called when a message is received by the subscription.
|
56
|
+
# * +qos_profile+ (QoSProfile or int) – A QoSProfile or a history depth to apply to the subscription. In the case that a history depth is provided, the QoS history is set to RMW_QOS_POLICY_HISTORY_KEEP_LAST, the QoS history depth is set to the value of the parameter, and all other QoS settings are set to their default values.
|
57
|
+
# * +callback_group (optional CallbackGroup) – The callback group for the subscription. If None, then the nodes default callback group is used.
|
58
|
+
def create_subscription(msg_type, topic, qos_profile, callback_group = nil, event_callbacks = nil, raw = false, &callback)
|
59
|
+
callback_group = @default_callback_group if callback_group.nil?
|
60
|
+
# * +event_callbacks (Optional[SubscriptionEventCallbacks]) – User-defined callbacks for middleware events.
|
61
|
+
if event_callbacks != nil
|
62
|
+
raise RclError.new("event_callbacks are not supported yet")
|
63
|
+
end
|
64
|
+
# * +raw (bool) – If True, then received messages will be stored in raw binary representation.
|
65
|
+
if raw != false
|
66
|
+
raise RclError.new("raw is not supported yet")
|
67
|
+
end
|
68
|
+
|
69
|
+
handle = CApi::rcl_get_zero_initialized_subscription()
|
70
|
+
subscription = Subscription.new(handle, @node_handle, msg_type, topic, callback, qos_profile, callback_group, event_callbacks, raw)
|
71
|
+
@subscriptions.append subscription
|
72
|
+
return subscription
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# Unsubscribe
|
77
|
+
# * +subscription+ a subscription created by create_subscription
|
78
|
+
def remove_subscription(subscription)
|
79
|
+
@subscriptions.remove subscription
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# Create a new service server.
|
84
|
+
#
|
85
|
+
# Parameters:
|
86
|
+
# * +srv_type+: The service type.
|
87
|
+
# * +srv_name+ (str): The name of the service.
|
88
|
+
# * +callback+ (block): A user-defined callback function that is called when a service request received by the server, it expects a request and response argument.
|
89
|
+
# * +qos_profile+ (QoSProfile): The quality of service profile to apply the service server.
|
90
|
+
# * +callback_group+ (optional CallbackGroup): The callback group for the service server. If None, then the nodes default callback group is used.
|
91
|
+
def create_service(srv_type, srv_name, qos_profile=QoSProfileServicesDefault, callback_group=nil, &callback)
|
92
|
+
callback_group = @default_callback_group if callback_group.nil?
|
93
|
+
handle = CApi::rcl_get_zero_initialized_service()
|
94
|
+
service = Service.new(handle, @node_handle, srv_type, srv_name, callback, qos_profile, callback_group)
|
95
|
+
@services.append(service)
|
96
|
+
return service
|
97
|
+
end
|
98
|
+
|
99
|
+
##
|
100
|
+
# Unsubscribe
|
101
|
+
# * +service+ a service created by create_service
|
102
|
+
def remove_service(service)
|
103
|
+
@services.remove service
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# Create a new service client.
|
108
|
+
# Parameters:
|
109
|
+
# * +srv_type+: The service type.
|
110
|
+
# * +srv_name+ (str): The name of the service.
|
111
|
+
# * +qos_profile+ (optional QoSProfile): The quality of service profile to apply the service client.
|
112
|
+
# * +callback_group+ (optional CallbackGroup): The callback group for the service client. If None, then the nodes default callback group is used
|
113
|
+
def create_client(srv_type, srv_name, qos_profile=QoSProfileServicesDefault)
|
114
|
+
handle = CApi::rcl_get_zero_initialized_client()
|
115
|
+
client = Client.new(handle, @node_handle, srv_type, srv_name, qos_profile)
|
116
|
+
return client
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# Create a new timer.
|
121
|
+
# The timer will be started and every timer_period_sec number of seconds the provided callback function will be called.
|
122
|
+
#
|
123
|
+
# Parameters:
|
124
|
+
# * +timer_period_sec+ (float): The period (s) of the timer.
|
125
|
+
# * +callback+ (block): A user-defined callback function that is called when the timer expires.
|
126
|
+
# * +callback_group+ (optional CallbackGroup): The callback group for the timer. If None, then the nodes default callback group is used.
|
127
|
+
# * +clock+ (optional Clock): The clock which the timer gets time from.
|
128
|
+
def create_timer(timer_period_sec, callback_group=nil, clock=Rclrb::RosClock, &callback)
|
129
|
+
callback_group = @default_callback_group if callback_group.nil?
|
130
|
+
handle = CApi::rcl_get_zero_initialized_timer()
|
131
|
+
timer = Timer.new(handle, @node_handle, timer_period_sec, callback, callback_group, clock)
|
132
|
+
@timers.append(timer)
|
133
|
+
return timer
|
134
|
+
end
|
135
|
+
|
136
|
+
##
|
137
|
+
# Stop a timer
|
138
|
+
# * +timer+ a timer created by create_timer
|
139
|
+
def remove_timer(timer)
|
140
|
+
@timers.remove timer
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Rclrb
|
2
|
+
##
|
3
|
+
# Represent a ROS publisher
|
4
|
+
class Publisher
|
5
|
+
attr_reader :msg_type
|
6
|
+
# Construct a new publisher, this should not be called directly, instead use Node.create_publisher.
|
7
|
+
def initialize(publisher_handle, node_handle, msg_type, topic, qos_profile, callback_group, event_callbacks)
|
8
|
+
@publisher_handle = publisher_handle
|
9
|
+
@node_handle = node_handle
|
10
|
+
@msg_type = msg_type
|
11
|
+
@topic = topic
|
12
|
+
@qos_profile = qos_profile
|
13
|
+
|
14
|
+
publisher_ops = CApi.rcl_publisher_get_default_options()
|
15
|
+
publisher_ops[:qos] = QoSProfile.get_profile(qos_profile).ros_profile
|
16
|
+
CApi.handle_result(CApi.rcl_publisher_init(@publisher_handle, node_handle, msg_type.type_support(), @topic, publisher_ops))
|
17
|
+
end
|
18
|
+
|
19
|
+
rclrb_finalize_with :@publisher_handle, :@node_handle do |publisher_handle, node_handle|
|
20
|
+
CApi.handle_result CApi.rcl_publisher_fini publisher_handle, node_handle
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Publish a message. If +msg+ has the wrong type, an exception is triggered.
|
25
|
+
def pub(msg)
|
26
|
+
raise InvalidMessageTypeError.new msg.class, @msg_type unless msg.kind_of? @msg_type
|
27
|
+
ros_msg = @msg_type.get_ros_message msg
|
28
|
+
CApi.handle_result(CApi.rcl_publish(@publisher_handle, ros_msg, nil), lambda { @msg_type.destroy_ros_message ros_msg})
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Return the number of subscription to that topic.
|
33
|
+
def subscription_count()
|
34
|
+
count = CApi::SizeTPtr.new
|
35
|
+
CApi.handle_result CApi.rcl_publisher_get_subscription_count(@publisher_handle, count)
|
36
|
+
return count[:value]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
data/lib/rclrb/qos.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
module Rclrb
|
2
|
+
module QosReliabilityPolicy
|
3
|
+
## Implementation specific default
|
4
|
+
SystemDefault = :RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT
|
5
|
+
## Guarantee that samples are delivered, may retry multiple times
|
6
|
+
Reliable = :RMW_QOS_POLICY_RELIABILITY_RELIABLE
|
7
|
+
## Attempt to deliver samples, but some may be lost if the network is not robust
|
8
|
+
BestEffort = :RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT
|
9
|
+
## Reliability policy has not yet been set
|
10
|
+
Unknown = :RMW_QOS_POLICY_RELIABILITY_UNKNOWN
|
11
|
+
end
|
12
|
+
module QosDurabilityPolicy
|
13
|
+
## Implementation specific default
|
14
|
+
SystemDefault = :RMW_QOS_POLICY_DURABILITY_SYSTEM_DEFAULT
|
15
|
+
## The rmw publisher is responsible for persisting samples for “late-joining” subscribers
|
16
|
+
TransientLocal = :RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL
|
17
|
+
## Samples are not persistent
|
18
|
+
Volatile = :RMW_QOS_POLICY_DURABILITY_VOLATILE
|
19
|
+
## History policy has not yet been set
|
20
|
+
Unknown = :RMW_QOS_POLICY_DURABILITY_UNKNOWN
|
21
|
+
end
|
22
|
+
module QosLivelinessPolicy
|
23
|
+
## Implementation specific default
|
24
|
+
SystemDefault = :RMW_QOS_POLICY_LIVELINESS_SYSTEM_DEFAULT
|
25
|
+
## The signal that establishes a Topic is alive comes from the ROS rmw layer
|
26
|
+
Automatic = :RMW_QOS_POLICY_LIVELINESS_AUTOMATIC
|
27
|
+
## The signal that establishes a Topic is alive is at the Topic level. Only publishing a message
|
28
|
+
# on the Topic or an explicit signal from the application to assert liveliness on the Topic
|
29
|
+
# will mark the Topic as being alive.
|
30
|
+
ManualByTopic = :RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC
|
31
|
+
## History policy has not yet been set
|
32
|
+
Unknown = :RMW_QOS_POLICY_LIVELINESS_UNKNOWN
|
33
|
+
end
|
34
|
+
module QosHistoryPolicy
|
35
|
+
## Implementation default for history policy
|
36
|
+
SystemDefault = :RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT
|
37
|
+
## Only store up to a maximum number of samples, dropping oldest once max is exceeded
|
38
|
+
KeepLast = :RMW_QOS_POLICY_HISTORY_KEEP_LAST
|
39
|
+
## Store all samples, subject to resource limits
|
40
|
+
KeepAll = :RMW_QOS_POLICY_HISTORY_KEEP_ALL
|
41
|
+
# History policy has not yet been set
|
42
|
+
Unknown = :RMW_QOS_POLICY_HISTORY_UNKNOWN
|
43
|
+
end
|
44
|
+
##
|
45
|
+
# Represent the QoS profile of a topic, service or action. Look at QoSProfileSensorData,
|
46
|
+
# QoSProfileParameters, QoSProfileDefault, QoSProfileServicesDefault, QoSProfileParameterEvents,
|
47
|
+
# or QoSProfileSystemDefault.
|
48
|
+
class QoSProfile
|
49
|
+
attr_reader :ros_profile
|
50
|
+
#
|
51
|
+
#
|
52
|
+
# Parameters:
|
53
|
+
# * +depth+ Size of the message queue.
|
54
|
+
# * +history+ History QoS policy setting
|
55
|
+
# * +reliability+ Reliabiilty QoS policy setting
|
56
|
+
# * +durability+ Durability QoS policy setting
|
57
|
+
# * +deadline+ The period at which messages are expected to be sent/received
|
58
|
+
# * +lifespan+ The age at which messages are considered expired and no longer valid
|
59
|
+
# * +liveliness+ Liveliness QoS policy setting
|
60
|
+
# * +liveliness_lease_duration+ The time within which the RMW node or publisher must show that it is alive
|
61
|
+
# * +avoid_ros_namespace_conventions+ If true, any ROS specific namespacing conventions will be circumvented
|
62
|
+
def initialize(depth:, history: QosHistoryPolicy::KeepLast, reliability: QosReliabilityPolicy::Reliable,
|
63
|
+
durability: QosDurabilityPolicy::Volatile, deadline: nil, lifespan: nil,
|
64
|
+
liveliness: QosLivelinessPolicy::SystemDefault, liveliness_lease_duration: nil,
|
65
|
+
avoid_ros_namespace_conventions: false)
|
66
|
+
@ros_profile = CApi::RmwQoSProfileT.new
|
67
|
+
@ros_profile[:depth] = depth
|
68
|
+
@ros_profile[:history] = history
|
69
|
+
@ros_profile[:reliability] = reliability
|
70
|
+
@ros_profile[:durability] = durability
|
71
|
+
unless deadline.nil?
|
72
|
+
s, n = deadline.to_sec_nsec
|
73
|
+
@ros_profile[:deadline][:sec] = s
|
74
|
+
@ros_profile[:deadline][:nsec] = n
|
75
|
+
end
|
76
|
+
unless lifespan.nil?
|
77
|
+
s, n = lifespan.to_sec_nsec
|
78
|
+
@ros_profile[:lifespan][:sec] = s
|
79
|
+
@ros_profile[:lifespan][:nsec] = n
|
80
|
+
end
|
81
|
+
@ros_profile[:lifespan]
|
82
|
+
@ros_profile[:liveliness] = liveliness
|
83
|
+
unless liveliness_lease_duration.nil?
|
84
|
+
s, n = liveliness_lease_duration.to_sec_nsec
|
85
|
+
@ros_profile[:liveliness_lease_duration][:sec] = s
|
86
|
+
@ros_profile[:liveliness_lease_duration][:nsec] = n
|
87
|
+
end
|
88
|
+
end
|
89
|
+
def QoSProfile.get_profile p
|
90
|
+
return p if p.kind_of? QoSProfile
|
91
|
+
return QoSProfile.new depth: p
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
## Qos profile to be used for sensor data,
|
96
|
+
QoSProfileSensorData = QoSProfile.new depth: 5, reliability: QosReliabilityPolicy::BestEffort
|
97
|
+
|
98
|
+
## Qos profile to be used for parameters.
|
99
|
+
QoSProfileParameters = QoSProfile.new depth: 1000
|
100
|
+
|
101
|
+
## Default Qos profile for topics.
|
102
|
+
QoSProfileDefault = QoSProfile.new depth: 10
|
103
|
+
|
104
|
+
## Default Qos profile for services.
|
105
|
+
QoSProfileServicesDefault = QoSProfile.new depth: 10
|
106
|
+
|
107
|
+
## Default Qos profile for events.
|
108
|
+
QoSProfileParameterEvents = QoSProfile.new depth: 1000
|
109
|
+
|
110
|
+
## Default Qos profile using system default (RMW implementation specific).
|
111
|
+
QoSProfileSystemDefault = QoSProfile.new depth: 5, history: QosHistoryPolicy::SystemDefault,
|
112
|
+
reliability: QosReliabilityPolicy::SystemDefault,
|
113
|
+
durability: QosDurabilityPolicy::SystemDefault,
|
114
|
+
liveliness: QosLivelinessPolicy::SystemDefault
|
115
|
+
|
116
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Rclrb
|
2
|
+
##
|
3
|
+
# Represent a ROS service
|
4
|
+
class Service
|
5
|
+
attr_reader :srv_type, :service_handle
|
6
|
+
# Construct a new service, this should not be called directly, instead use Node.create_service.
|
7
|
+
def initialize(service_handle, node_handle, srv_type, srv_name, callback, qos_profile, callback_group)
|
8
|
+
@service_handle = service_handle
|
9
|
+
@srv_type = srv_type
|
10
|
+
@srv_name = srv_name
|
11
|
+
@callback = callback
|
12
|
+
@callback_group = callback_group
|
13
|
+
@qos_profile = qos_profile
|
14
|
+
callback_group.add self
|
15
|
+
|
16
|
+
service_ops = CApi.rcl_service_get_default_options()
|
17
|
+
service_ops[:qos] = QoSProfile.get_profile(qos_profile).ros_profile
|
18
|
+
CApi.handle_result CApi.rcl_service_init(@service_handle, node_handle, srv_type.type_support(), @srv_name, service_ops)
|
19
|
+
end
|
20
|
+
|
21
|
+
rclrb_finalize_with :@service_handle, :@node_handle do |service_handle, node_handle|
|
22
|
+
CApi.handle_result CApi.rcl_service_fini service_handle, node_handle
|
23
|
+
end
|
24
|
+
|
25
|
+
def spin(wait_set = nil)
|
26
|
+
wait_set.add self if wait_set
|
27
|
+
request_id = CApi::RmwRequestIdT.new
|
28
|
+
ros_req = @srv_type::Request::FFIType.new
|
29
|
+
status = CApi.rcl_take_request @service_handle, request_id, ros_req
|
30
|
+
|
31
|
+
if status == CApi::RCL_RET_OK
|
32
|
+
request = @srv_type::Request.parse_ros_message ros_req
|
33
|
+
@srv_type::Request.destroy_ros_message ros_req
|
34
|
+
response = @srv_type::Response.new
|
35
|
+
@callback.call request, response
|
36
|
+
ros_resp = @srv_type::Response.get_ros_message response
|
37
|
+
CApi.handle_result CApi.rcl_send_response(@service_handle, request_id, ros_resp), lambda { @srv_type::Response.destroy_ros_message ros_resp}
|
38
|
+
|
39
|
+
elsif status == CApi::RCL_RET_SERVICE_TAKE_FAILED
|
40
|
+
# no service received
|
41
|
+
else
|
42
|
+
CApi.handle_result(status)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Rclrb
|
2
|
+
##
|
3
|
+
# Represent a ROS subscription to a topic
|
4
|
+
class Subscription
|
5
|
+
attr_reader :msg_type, :subscription_handle
|
6
|
+
# Construct a new subscription, this should not be called directly, instead use Node.create_subscription.
|
7
|
+
def initialize(subscription_handle, node_handle, msg_type, topic, callback, qos_profile, callback_group, event_callbacks, raw)
|
8
|
+
@subscription_handle = subscription_handle
|
9
|
+
@node_handle = node_handle
|
10
|
+
@msg_type = msg_type
|
11
|
+
@topic = topic
|
12
|
+
@callback = callback
|
13
|
+
@qos_profile = qos_profile
|
14
|
+
@raw = raw
|
15
|
+
callback_group.add self
|
16
|
+
|
17
|
+
subscription_ops = CApi.rcl_subscription_get_default_options()
|
18
|
+
subscription_ops[:qos] = QoSProfile.get_profile(qos_profile).ros_profile
|
19
|
+
CApi.handle_result CApi.rcl_subscription_init(@subscription_handle, node_handle, msg_type.type_support(), @topic, subscription_ops)
|
20
|
+
end
|
21
|
+
rclrb_finalize_with :@subscription_handle, :@node_handle do |subscription_handle, node_handle|
|
22
|
+
CApi.handle_result CApi.rcl_subscription_fini(subscription_handle, node_handle)
|
23
|
+
end
|
24
|
+
def raw?
|
25
|
+
return @raw
|
26
|
+
end
|
27
|
+
|
28
|
+
def spin(wait_set = nil)
|
29
|
+
wait_set.add self if wait_set
|
30
|
+
message_info = CApi::RmwMessageInfoT.new
|
31
|
+
data = @msg_type::FFIType.new
|
32
|
+
status = CApi.rcl_take @subscription_handle, data, message_info, nil
|
33
|
+
|
34
|
+
if status == CApi::RCL_RET_OK
|
35
|
+
msg = @msg_type.parse_ros_message data
|
36
|
+
@msg_type.destroy_ros_message data
|
37
|
+
@callback.call(msg)
|
38
|
+
elsif status == CApi::RCL_RET_SUBSCRIPTION_TAKE_FAILED
|
39
|
+
# no message received
|
40
|
+
else
|
41
|
+
CApi.handle_result status
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/rclrb/time.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module Rclrb
|
2
|
+
##
|
3
|
+
# Represent time.
|
4
|
+
class Time
|
5
|
+
##
|
6
|
+
# Initialise time, +time+ is given in seconds
|
7
|
+
def initialize time = 0
|
8
|
+
@time_sec = time
|
9
|
+
end
|
10
|
+
##
|
11
|
+
# Create a time objects from seconds
|
12
|
+
def Time.from_sec sec
|
13
|
+
return Time.new sec
|
14
|
+
end
|
15
|
+
##
|
16
|
+
# Create a time objects from seconds and nanoseconds
|
17
|
+
def Time.from_sec_nsec sec, nsec
|
18
|
+
return Time.new sec + nsec * 1e-9
|
19
|
+
end
|
20
|
+
##
|
21
|
+
# Return the time in seconds
|
22
|
+
def to_sec
|
23
|
+
return @time_sec
|
24
|
+
end
|
25
|
+
##
|
26
|
+
# Return the time in second and nanoseconds (as integers)
|
27
|
+
def to_sec_nsec
|
28
|
+
si = @time_sec.to_i
|
29
|
+
return si, ((@time_sec - si) * 1e9).to_i
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/rclrb/timer.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Rclrb
|
2
|
+
##
|
3
|
+
# Represent a ROS Timer
|
4
|
+
class Timer
|
5
|
+
attr_reader :timer_handle
|
6
|
+
# Construct a new timer, this should not be called directly, instead use Node.create_subscription.
|
7
|
+
def initialize(handle, node_handle, timer_period_sec, callback, callback_group, clock)
|
8
|
+
@callback_proc = Proc.new do |timer, time|
|
9
|
+
callback.call time
|
10
|
+
end
|
11
|
+
@timer_handle = handle
|
12
|
+
@node_handle = node_handle
|
13
|
+
callback_group.add self
|
14
|
+
|
15
|
+
CApi.handle_result(CApi.rcl_timer_init(handle, clock.handle, Rclrb.rcl_context, timer_period_sec*1e9, @callback_proc, Rclrb.rcl_allocator))
|
16
|
+
end
|
17
|
+
|
18
|
+
rclrb_finalize_with :@timer_handle, :@node_handle do |timer_handle, node_handle|
|
19
|
+
CApi.handle_result CApi.rcl_timer_fini timer_handle, node_handle
|
20
|
+
end
|
21
|
+
|
22
|
+
def spin(wait_set = nil)
|
23
|
+
wait_set.add self if wait_set
|
24
|
+
is_ready = CApi::BoolPtr.new
|
25
|
+
CApi.handle_result(CApi.rcl_timer_is_ready @timer_handle, is_ready)
|
26
|
+
if is_ready[:value]
|
27
|
+
CApi.handle_result(CApi.rcl_timer_call @timer_handle)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Rclrb
|
2
|
+
##
|
3
|
+
# This class is use to Wait for events.
|
4
|
+
class WaitSet
|
5
|
+
def initialize
|
6
|
+
@subscriptions = []
|
7
|
+
@services = []
|
8
|
+
@clients = []
|
9
|
+
@timers = []
|
10
|
+
end
|
11
|
+
##
|
12
|
+
# Wait for an event on +ẃhat+, every +timeout+ evaluate +block+ and if the value
|
13
|
+
# returned is true, then exit the function otherwise continue waiting.
|
14
|
+
def WaitSet.wait_for what, timeout = -1, &block
|
15
|
+
ws = WaitSet.new
|
16
|
+
ws.add what
|
17
|
+
until block.call
|
18
|
+
ws.wait timeout
|
19
|
+
end
|
20
|
+
end
|
21
|
+
##
|
22
|
+
# Add an item (Subscription, Service...) to the wait set
|
23
|
+
def add(what)
|
24
|
+
if what.kind_of? Subscription
|
25
|
+
@subscriptions.append what
|
26
|
+
elsif what.kind_of? Service
|
27
|
+
@services.append what
|
28
|
+
elsif what.kind_of? Client
|
29
|
+
@clients.append what
|
30
|
+
elsif what.kind_of? Timer
|
31
|
+
@timers.append what
|
32
|
+
end
|
33
|
+
end
|
34
|
+
##
|
35
|
+
# Wait until +timeout+ or an event occured in one of the item added with add.
|
36
|
+
# If +timeout+ is equalt to -1, this function will block until an event occured or indefinitely.
|
37
|
+
def wait(timeout = -1)
|
38
|
+
ws = CApi.rcl_get_zero_initialized_wait_set
|
39
|
+
CApi.handle_result CApi.rcl_wait_set_init(ws, @subscriptions.size, 1, @timers.size, @clients.size, @services.size, 0, Rclrb.rcl_context, Rclrb.rcl_allocator)
|
40
|
+
begin
|
41
|
+
@subscriptions.each() { |x| CApi.handle_result CApi.rcl_wait_set_add_subscription(ws, x.subscription_handle, nil)}
|
42
|
+
CApi.handle_result CApi.rcl_wait_set_add_guard_condition(ws, Rclrb.rcl_signal_guard_condition.guard_condition_handle, nil)
|
43
|
+
@services.each() { |x| CApi.handle_result CApi.rcl_wait_set_add_service(ws, x.service_handle, nil)}
|
44
|
+
@clients.each() { |x| CApi.handle_result CApi.rcl_wait_set_add_client(ws, x.client_handle, nil)}
|
45
|
+
@timers.each() { |x| CApi.handle_result CApi.rcl_wait_set_add_timer(ws, x.timer_handle, nil)}
|
46
|
+
# Wait has to run in a seperate thread to allow for signals to work
|
47
|
+
status = nil
|
48
|
+
t = Thread.new do
|
49
|
+
status = CApi.rcl_wait(ws, timeout)
|
50
|
+
end
|
51
|
+
t.join
|
52
|
+
if status != CApi::RCL_RET_OK and status != CApi::RCL_RET_TIMEOUT
|
53
|
+
CApi.handle_result status
|
54
|
+
end
|
55
|
+
rescue RclError
|
56
|
+
CApi.handle_result CApi.rcl_wait_set_fini(ws)
|
57
|
+
raise
|
58
|
+
end
|
59
|
+
CApi.handle_result CApi.rcl_wait_set_fini(ws)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/rclrb.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
require_relative 'rclrb/common'
|
4
|
+
require_relative 'rclrb/internal'
|
5
|
+
require_relative 'rclrb/version'
|
6
|
+
|
7
|
+
require_relative 'rclrb/capi'
|
8
|
+
|
9
|
+
require_relative 'rclrb/callback_group'
|
10
|
+
require_relative 'rclrb/clock'
|
11
|
+
require_relative 'rclrb/client'
|
12
|
+
require_relative 'rclrb/init'
|
13
|
+
require_relative 'rclrb/executor'
|
14
|
+
require_relative 'rclrb/fields'
|
15
|
+
require_relative 'rclrb/future'
|
16
|
+
require_relative 'rclrb/guard_condition'
|
17
|
+
require_relative 'rclrb/interfaces'
|
18
|
+
require_relative 'rclrb/node'
|
19
|
+
require_relative 'rclrb/publisher'
|
20
|
+
require_relative 'rclrb/qos'
|
21
|
+
require_relative 'rclrb/subscription'
|
22
|
+
require_relative 'rclrb/time'
|
23
|
+
require_relative 'rclrb/timer'
|
24
|
+
require_relative 'rclrb/service'
|
25
|
+
require_relative 'rclrb/wait_set'
|
26
|
+
|
27
|
+
module Kernel
|
28
|
+
# make an alias of the original require
|
29
|
+
alias_method :rclrb_original_require, :require
|
30
|
+
|
31
|
+
# rewrite require
|
32
|
+
LoadedRosInterfacePackages = []
|
33
|
+
def require name
|
34
|
+
begin
|
35
|
+
rclrb_original_require name
|
36
|
+
rescue LoadError
|
37
|
+
unless LoadedRosInterfacePackages.include? name
|
38
|
+
raise unless Rclrb::Interfaces.load_definitions name
|
39
|
+
LoadedRosInterfacePackages.append(name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rclrb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Cyrille Berger
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-09-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ffi
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.15'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.15'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.0'
|
41
|
+
description: 'Allows to communicate with ROS program.
|
42
|
+
|
43
|
+
'
|
44
|
+
email:
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files:
|
48
|
+
- README.md
|
49
|
+
- COPYING
|
50
|
+
files:
|
51
|
+
- COPYING
|
52
|
+
- README.md
|
53
|
+
- lib/rclrb.rb
|
54
|
+
- lib/rclrb/callback_group.rb
|
55
|
+
- lib/rclrb/capi.rb
|
56
|
+
- lib/rclrb/client.rb
|
57
|
+
- lib/rclrb/clock.rb
|
58
|
+
- lib/rclrb/common.rb
|
59
|
+
- lib/rclrb/executor.rb
|
60
|
+
- lib/rclrb/fields.rb
|
61
|
+
- lib/rclrb/future.rb
|
62
|
+
- lib/rclrb/guard_condition.rb
|
63
|
+
- lib/rclrb/init.rb
|
64
|
+
- lib/rclrb/interfaces.rb
|
65
|
+
- lib/rclrb/internal.rb
|
66
|
+
- lib/rclrb/node.rb
|
67
|
+
- lib/rclrb/publisher.rb
|
68
|
+
- lib/rclrb/qos.rb
|
69
|
+
- lib/rclrb/service.rb
|
70
|
+
- lib/rclrb/subscription.rb
|
71
|
+
- lib/rclrb/time.rb
|
72
|
+
- lib/rclrb/timer.rb
|
73
|
+
- lib/rclrb/version.rb
|
74
|
+
- lib/rclrb/wait_set.rb
|
75
|
+
homepage: https://gitlab.com/cyloncore/rclrb
|
76
|
+
licenses:
|
77
|
+
- MPL-2.0
|
78
|
+
metadata: {}
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options: []
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
requirements: []
|
94
|
+
rubygems_version: 3.3.5
|
95
|
+
signing_key:
|
96
|
+
specification_version: 4
|
97
|
+
summary: Ruby bindings for Ros Communication Library (ROS2)
|
98
|
+
test_files: []
|