rclrb 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|