asterism 0.3.0 → 0.4.1
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 +4 -4
- data/CHANGELOG.md +149 -0
- data/NOTICE +12 -7
- data/README.md +305 -74
- data/data/msgs/LICENSE-BSD-3-Clause-tf2_msgs.txt +29 -0
- data/data/msgs/NOTICE +19 -13
- data/data/msgs/builtin_interfaces/msg/Duration.rb +4 -0
- data/data/msgs/builtin_interfaces/msg/Time.rb +4 -0
- data/data/msgs/diagnostic_msgs/msg/DiagnosticArray.rb +40 -0
- data/data/msgs/diagnostic_msgs/msg/DiagnosticStatus.rb +55 -0
- data/data/msgs/diagnostic_msgs/msg/KeyValue.rb +37 -0
- data/data/msgs/example_interfaces/srv/AddTwoInts.rb +7 -0
- data/data/msgs/geometry_msgs/msg/Accel.rb +4 -0
- data/data/msgs/geometry_msgs/msg/AccelStamped.rb +4 -0
- data/data/msgs/geometry_msgs/msg/Point.rb +5 -0
- data/data/msgs/geometry_msgs/msg/Point32.rb +5 -0
- data/data/msgs/geometry_msgs/msg/PointStamped.rb +4 -0
- data/data/msgs/geometry_msgs/msg/Pose.rb +4 -0
- data/data/msgs/geometry_msgs/msg/Pose2D.rb +5 -0
- data/data/msgs/geometry_msgs/msg/PoseStamped.rb +4 -0
- data/data/msgs/geometry_msgs/msg/PoseWithCovariance.rb +39 -0
- data/data/msgs/geometry_msgs/msg/Quaternion.rb +6 -0
- data/data/msgs/geometry_msgs/msg/QuaternionStamped.rb +4 -0
- data/data/msgs/geometry_msgs/msg/Transform.rb +4 -0
- data/data/msgs/geometry_msgs/msg/TransformStamped.rb +5 -0
- data/data/msgs/geometry_msgs/msg/Twist.rb +4 -0
- data/data/msgs/geometry_msgs/msg/TwistStamped.rb +4 -0
- data/data/msgs/geometry_msgs/msg/TwistWithCovariance.rb +39 -0
- data/data/msgs/geometry_msgs/msg/Vector3.rb +5 -0
- data/data/msgs/geometry_msgs/msg/Vector3Stamped.rb +4 -0
- data/data/msgs/geometry_msgs/msg/Wrench.rb +4 -0
- data/data/msgs/geometry_msgs/msg/WrenchStamped.rb +4 -0
- data/data/msgs/nav_msgs/msg/MapMetaData.rb +52 -0
- data/data/msgs/nav_msgs/msg/OccupancyGrid.rb +44 -0
- data/data/msgs/nav_msgs/msg/Odometry.rb +49 -0
- data/data/msgs/nav_msgs/msg/Path.rb +40 -0
- data/data/msgs/rcl_interfaces/msg/Log.rb +64 -0
- data/data/msgs/rcl_interfaces/msg/Parameter.rb +39 -0
- data/data/msgs/rcl_interfaces/msg/ParameterEvent.rb +52 -0
- data/data/msgs/rcl_interfaces/msg/ParameterValue.rb +69 -0
- data/data/msgs/sensor_msgs/msg/BatteryState.rb +18 -0
- data/data/msgs/sensor_msgs/msg/CompressedImage.rb +43 -0
- data/data/msgs/sensor_msgs/msg/FluidPressure.rb +5 -0
- data/data/msgs/sensor_msgs/msg/Illuminance.rb +5 -0
- data/data/msgs/sensor_msgs/msg/Imu.rb +9 -0
- data/data/msgs/sensor_msgs/msg/JointState.rb +7 -0
- data/data/msgs/sensor_msgs/msg/LaserScan.rb +71 -0
- data/data/msgs/sensor_msgs/msg/MagneticField.rb +5 -0
- data/data/msgs/sensor_msgs/msg/NavSatFix.rb +9 -0
- data/data/msgs/sensor_msgs/msg/NavSatStatus.rb +4 -0
- data/data/msgs/sensor_msgs/msg/PointCloud2.rb +68 -0
- data/data/msgs/sensor_msgs/msg/PointField.rb +53 -0
- data/data/msgs/sensor_msgs/msg/Range.rb +9 -0
- data/data/msgs/sensor_msgs/msg/RelativeHumidity.rb +5 -0
- data/data/msgs/sensor_msgs/msg/Temperature.rb +5 -0
- data/data/msgs/std_msgs/msg/Bool.rb +3 -0
- data/data/msgs/std_msgs/msg/Byte.rb +3 -0
- data/data/msgs/std_msgs/msg/ByteMultiArray.rb +4 -0
- data/data/msgs/std_msgs/msg/Char.rb +3 -0
- data/data/msgs/std_msgs/msg/ColorRGBA.rb +6 -0
- data/data/msgs/std_msgs/msg/Empty.rb +1 -0
- data/data/msgs/std_msgs/msg/Float32.rb +3 -0
- data/data/msgs/std_msgs/msg/Float32MultiArray.rb +4 -0
- data/data/msgs/std_msgs/msg/Float64.rb +3 -0
- data/data/msgs/std_msgs/msg/Float64MultiArray.rb +4 -0
- data/data/msgs/std_msgs/msg/Header.rb +4 -0
- data/data/msgs/std_msgs/msg/Int16.rb +3 -0
- data/data/msgs/std_msgs/msg/Int16MultiArray.rb +4 -0
- data/data/msgs/std_msgs/msg/Int32.rb +3 -0
- data/data/msgs/std_msgs/msg/Int32MultiArray.rb +4 -0
- data/data/msgs/std_msgs/msg/Int64.rb +3 -0
- data/data/msgs/std_msgs/msg/Int64MultiArray.rb +4 -0
- data/data/msgs/std_msgs/msg/Int8.rb +3 -0
- data/data/msgs/std_msgs/msg/Int8MultiArray.rb +4 -0
- data/data/msgs/std_msgs/msg/MultiArrayDimension.rb +5 -0
- data/data/msgs/std_msgs/msg/MultiArrayLayout.rb +4 -0
- data/data/msgs/std_msgs/msg/String.rb +3 -0
- data/data/msgs/std_msgs/msg/UInt16.rb +3 -0
- data/data/msgs/std_msgs/msg/UInt16MultiArray.rb +4 -0
- data/data/msgs/std_msgs/msg/UInt32.rb +3 -0
- data/data/msgs/std_msgs/msg/UInt32MultiArray.rb +4 -0
- data/data/msgs/std_msgs/msg/UInt64.rb +3 -0
- data/data/msgs/std_msgs/msg/UInt64MultiArray.rb +4 -0
- data/data/msgs/std_msgs/msg/UInt8.rb +3 -0
- data/data/msgs/std_msgs/msg/UInt8MultiArray.rb +4 -0
- data/data/msgs/tf2_msgs/msg/TFMessage.rb +35 -0
- data/data/msgs/visualization_msgs/msg/Marker.rb +132 -0
- data/data/msgs/visualization_msgs/msg/MarkerArray.rb +35 -0
- data/data/msgs/visualization_msgs/msg/MeshFile.rb +37 -0
- data/data/msgs/visualization_msgs/msg/UVCoordinate.rb +37 -0
- data/lib/asterism/cruby/objects.rb +25 -10
- data/lib/asterism/cruby/ros.rb +6 -4
- data/lib/asterism/cruby/zenoh.rb +190 -60
- data/lib/asterism/version.rb +1 -1
- data/lib/asterism.rb +2 -2
- data/mrblib/asterism.rb +112 -19
- data/mrblib/cdr.rb +5 -1
- data/mrblib/future.rb +11 -2
- data/mrblib/proxy.rb +13 -3
- data/mrblib/ros.rb +189 -44
- data/tools/asterism_msggen.rb +32 -5
- metadata +29 -4
data/mrblib/ros.rb
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
# sub.each_pending { |msg, info| puts msg.data } # info: Attachment or nil
|
|
21
21
|
# end
|
|
22
22
|
# cli.call(a: 2, b: 3).sum # waits (polling), or
|
|
23
|
+
# cli.call(request: {a: 2, b: 3}, timeout: 5.0) # the request as a Hash
|
|
23
24
|
# c = cli.call_async(a: 2, b: 3); c.done?; c.value
|
|
24
25
|
# node.close
|
|
25
26
|
#
|
|
@@ -47,6 +48,11 @@
|
|
|
47
48
|
# Nothing here depends on the host: it uses an Asterism::Zenoh::Session
|
|
48
49
|
# (put with attachment:, subscribe, liveliness, zid) and Asterism::CDR.
|
|
49
50
|
module Asterism
|
|
51
|
+
# (asterism.rb defines both; this file is also loaded on its own, with
|
|
52
|
+
# cdr.rb, by the type tools.)
|
|
53
|
+
class Error < StandardError; end
|
|
54
|
+
class TimeoutError < Error; end
|
|
55
|
+
|
|
50
56
|
module ROS
|
|
51
57
|
LIVELINESS_ROOT = "@ros2_lv"
|
|
52
58
|
# rmw_zenoh's QoS chunk for the default profile (reliable, volatile,
|
|
@@ -120,8 +126,20 @@ module Asterism
|
|
|
120
126
|
out
|
|
121
127
|
end
|
|
122
128
|
|
|
123
|
-
# A service call got no answer in time (or nobody serves that name).
|
|
124
|
-
|
|
129
|
+
# A service call got no answer in time (or nobody serves that name). An
|
|
130
|
+
# Asterism::TimeoutError (so an Asterism::Error); its old name
|
|
131
|
+
# Asterism::ROS::Timeout still works, with a deprecation warning.
|
|
132
|
+
class TimeoutError < ::Asterism::TimeoutError; end
|
|
133
|
+
|
|
134
|
+
# The old name Asterism::ROS::Timeout (deprecated): see
|
|
135
|
+
# Asterism.const_missing.
|
|
136
|
+
def self.const_missing(name)
|
|
137
|
+
if name == :Timeout
|
|
138
|
+
::Asterism.deprecated("Asterism::ROS::Timeout", "Asterism::ROS::TimeoutError")
|
|
139
|
+
return TimeoutError
|
|
140
|
+
end
|
|
141
|
+
super
|
|
142
|
+
end
|
|
125
143
|
|
|
126
144
|
# Milliseconds from an arbitrary origin (the board's clock when there is
|
|
127
145
|
# one), and a short pause; the same helpers Asterism's calls use.
|
|
@@ -180,7 +198,7 @@ module Asterism
|
|
|
180
198
|
TYPE_PATH = ["/usr/share/asterism/msgs"]
|
|
181
199
|
|
|
182
200
|
# The type has no generated file in TYPE_PATH.
|
|
183
|
-
class UnknownType < ::
|
|
201
|
+
class UnknownType < ::Asterism::Error; end
|
|
184
202
|
|
|
185
203
|
def self.type_path
|
|
186
204
|
TYPE_PATH
|
|
@@ -191,11 +209,14 @@ module Asterism
|
|
|
191
209
|
# returns it (Asterism::ROS::GeometryMsgs::Twist). The file loads the
|
|
192
210
|
# types it is made of first. A type already defined (loaded before, or
|
|
193
211
|
# written in Ruby by the application) is returned without a file.
|
|
212
|
+
#
|
|
213
|
+
# The name is checked before it reaches the file system (it may come
|
|
214
|
+
# from the network): pkg/msg/Name or pkg/srv/Name, the package of
|
|
215
|
+
# a-z, 0-9 and _ starting with a letter, the type of letters and digits
|
|
216
|
+
# starting with a capital (the rules of ROS 2's interface names).
|
|
217
|
+
# Anything else raises ArgumentError.
|
|
194
218
|
def self.require_type(name)
|
|
195
|
-
parts = name
|
|
196
|
-
if parts.size != 3 || (parts[1] != "msg" && parts[1] != "srv")
|
|
197
|
-
raise ArgumentError, "bad type name #{name.inspect} (want pkg/msg/Name or pkg/srv/Name)"
|
|
198
|
-
end
|
|
219
|
+
parts = check_type_name(name)
|
|
199
220
|
t = type_constant(parts[0], parts[2])
|
|
200
221
|
return t if t
|
|
201
222
|
found = nil
|
|
@@ -209,6 +230,68 @@ module Asterism
|
|
|
209
230
|
t
|
|
210
231
|
end
|
|
211
232
|
|
|
233
|
+
# "pkg/msg/Name" -> ["pkg", "msg", "Name"], or ArgumentError. By hand,
|
|
234
|
+
# byte by byte: the boards have no Regexp.
|
|
235
|
+
# @api private
|
|
236
|
+
def self.check_type_name(name)
|
|
237
|
+
s = name.to_s
|
|
238
|
+
parts = s.split("/")
|
|
239
|
+
# (split drops a trailing "/": the parts must give the name back)
|
|
240
|
+
ok = parts.size == 3 && parts.join("/") == s && (parts[1] == "msg" || parts[1] == "srv") &&
|
|
241
|
+
ros_name_part?(parts[0], false) && ros_name_part?(parts[2], true)
|
|
242
|
+
raise ArgumentError, "bad type name #{name.inspect} (want pkg/msg/Name or pkg/srv/Name)" unless ok
|
|
243
|
+
parts
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# A package name ([a-z][a-z0-9_]*) or, with type true, a type name
|
|
247
|
+
# ([A-Z][A-Za-z0-9]*).
|
|
248
|
+
# @api private
|
|
249
|
+
def self.ros_name_part?(s, type)
|
|
250
|
+
n = s.bytesize
|
|
251
|
+
return false if n == 0
|
|
252
|
+
c = s.getbyte(0)
|
|
253
|
+
return false unless type ? (c >= 65 && c <= 90) : (c >= 97 && c <= 122)
|
|
254
|
+
i = 1
|
|
255
|
+
while i < n
|
|
256
|
+
c = s.getbyte(i)
|
|
257
|
+
ok = (c >= 97 && c <= 122) || (c >= 48 && c <= 57) ||
|
|
258
|
+
(type ? (c >= 65 && c <= 90) : c == 95)
|
|
259
|
+
return false unless ok
|
|
260
|
+
i += 1
|
|
261
|
+
end
|
|
262
|
+
true
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# What the fields of a type and of every message type it is made of
|
|
266
|
+
# are: {ROS name => FIELD_TYPES}, the type first, each type once. The
|
|
267
|
+
# type is a generated type or its ROS name (the types are loaded with
|
|
268
|
+
# require_type); for a service, its Request and Response. Each entry of
|
|
269
|
+
# FIELD_TYPES is [name, base, kind, nested, capacity, string_capacity]
|
|
270
|
+
# (plain Strings, Integers and nil; see tools/asterism_msggen.rb):
|
|
271
|
+
# Asterism::ROS.field_types("sensor_msgs/msg/JointState")
|
|
272
|
+
# # => {"sensor_msgs/msg/JointState" => [["header", "std_msgs/msg/Header",
|
|
273
|
+
# # "scalar", "std_msgs/msg/Header", nil, nil],
|
|
274
|
+
# # ["name", "string", "sequence", nil, nil, nil], ...],
|
|
275
|
+
# # "std_msgs/msg/Header" => [...], "builtin_interfaces/msg/Time" => [...]}
|
|
276
|
+
# A type without FIELD_TYPES (written by hand, or generated before
|
|
277
|
+
# 0.4.1) raises UnknownType.
|
|
278
|
+
def self.field_types(type)
|
|
279
|
+
t = type_of(type)
|
|
280
|
+
queue = t.is_a?(::Class) ? [t] : [t::Request, t::Response]
|
|
281
|
+
out = {}
|
|
282
|
+
until queue.empty?
|
|
283
|
+
c = queue.shift
|
|
284
|
+
next if out.key?(c::ROS_NAME)
|
|
285
|
+
unless c.const_defined?(:FIELD_TYPES, false)
|
|
286
|
+
raise UnknownType, "#{c::ROS_NAME} has no FIELD_TYPES (generate it with asterism_msggen.rb of 0.4.1 or later)"
|
|
287
|
+
end
|
|
288
|
+
fts = c::FIELD_TYPES
|
|
289
|
+
out[c::ROS_NAME] = fts
|
|
290
|
+
fts.each { |f| queue << require_type(f[3]) if f[3] }
|
|
291
|
+
end
|
|
292
|
+
out
|
|
293
|
+
end
|
|
294
|
+
|
|
212
295
|
# On mruby the file is evaluated in this VM (Kernel#eval): unlike
|
|
213
296
|
# require, which runs each file in a Sandbox task of its own that stays
|
|
214
297
|
# for the life of the VM, this keeps only the compiled code (measured in
|
|
@@ -252,8 +335,8 @@ module Asterism
|
|
|
252
335
|
end
|
|
253
336
|
|
|
254
337
|
# The base of the generated message types (Request / Response of a
|
|
255
|
-
# service too). A type adds ROS_NAME, TYPE_NAME, TYPE_HASH, FIELDS,
|
|
256
|
-
# accessor per field, initialize(**fields) and the CDR steps
|
|
338
|
+
# service too). A type adds ROS_NAME, TYPE_NAME, TYPE_HASH, FIELDS,
|
|
339
|
+
# FIELD_TYPES (0.4.1 and later), an accessor per field, initialize(**fields) and the CDR steps
|
|
257
340
|
# write(w, msg) / read(r); the rest is here.
|
|
258
341
|
class Message
|
|
259
342
|
# A message from nil (all defaults), a Hash of fields (Symbol or
|
|
@@ -381,22 +464,26 @@ module Asterism
|
|
|
381
464
|
# the block, from node.poll. Returns the Subscription (close ends it).
|
|
382
465
|
# The block runs on the update loop's stack: do not wait in it (no
|
|
383
466
|
# service call, no proxy call); start a call_async and look at it
|
|
384
|
-
# later instead.
|
|
467
|
+
# later instead. Without a block, the same as subscription: a
|
|
468
|
+
# Subscription to take from with each_pending.
|
|
385
469
|
def subscribe(topic, type, qos: DEFAULT_QOS, depth: 16, &handler)
|
|
386
|
-
|
|
470
|
+
return subscription(topic, type, qos: qos, depth: depth) unless handler
|
|
387
471
|
e = subscription(topic, type, qos: qos, depth: depth)
|
|
388
472
|
e.handler = handler
|
|
389
473
|
@handled << e
|
|
390
474
|
e
|
|
391
475
|
end
|
|
392
476
|
|
|
393
|
-
# Calls the block every `seconds` (
|
|
394
|
-
# from node.poll. Returns a Timer (cancel).
|
|
395
|
-
# a long stall it starts again from now
|
|
396
|
-
# times at once. As for subscribe: do not
|
|
397
|
-
|
|
477
|
+
# Calls the block every `seconds` (or every ms: milliseconds; the first
|
|
478
|
+
# time one period from now), from node.poll. Returns a Timer (cancel).
|
|
479
|
+
# It keeps the period; after a long stall it starts again from now
|
|
480
|
+
# instead of firing the missed times at once. As for subscribe: do not
|
|
481
|
+
# wait in the block.
|
|
482
|
+
def every(seconds = nil, ms: nil, &blk)
|
|
398
483
|
raise ArgumentError, "every needs a block" unless blk
|
|
399
|
-
|
|
484
|
+
raise ArgumentError, "every: give the period once (seconds, or ms:)" if !seconds.nil? && !ms.nil?
|
|
485
|
+
raise ArgumentError, "every: a period is needed (seconds, or ms:)" if seconds.nil? && ms.nil?
|
|
486
|
+
t = ::Asterism::ROS::Timer.new(self, seconds, blk, ms)
|
|
400
487
|
@timers << t
|
|
401
488
|
t
|
|
402
489
|
end
|
|
@@ -407,15 +494,17 @@ module Asterism
|
|
|
407
494
|
end
|
|
408
495
|
|
|
409
496
|
# node.call("/add_two_ints", AddTwoInts, a: 1, b: 2) -> response.
|
|
410
|
-
#
|
|
411
|
-
|
|
497
|
+
# The request as keywords, as a Hash or message (positional or
|
|
498
|
+
# request:); the time limit as timeout: (seconds) or timeout_ms:. A
|
|
499
|
+
# client per service name is made on first use and kept.
|
|
500
|
+
def call(service, type, req = nil, request: nil, timeout: nil, timeout_ms: nil, **fields)
|
|
412
501
|
@clients ||= {}
|
|
413
502
|
c = @clients[service]
|
|
414
503
|
if c.nil? || c.closed?
|
|
415
504
|
c = client(service, type)
|
|
416
505
|
@clients[service] = c
|
|
417
506
|
end
|
|
418
|
-
c.call(request, timeout_ms: timeout_ms, **fields)
|
|
507
|
+
c.call(req, request: request, timeout: timeout, timeout_ms: timeout_ms, **fields)
|
|
419
508
|
end
|
|
420
509
|
|
|
421
510
|
# Polls the session (Asterism::Zenoh::Session#poll), answers the
|
|
@@ -432,6 +521,7 @@ module Asterism
|
|
|
432
521
|
|
|
433
522
|
# What a waiting service call polls: the session and the services, not
|
|
434
523
|
# the subscribe / every blocks (those run from node.poll only).
|
|
524
|
+
# @api private
|
|
435
525
|
def pump(steps = 8)
|
|
436
526
|
ok = @session.poll(steps)
|
|
437
527
|
i = 0
|
|
@@ -444,7 +534,7 @@ module Asterism
|
|
|
444
534
|
|
|
445
535
|
# While loops, not each with a block: the application's blocks are
|
|
446
536
|
# called from here, and nothing else should stand between them and
|
|
447
|
-
# the update loop on the stack.
|
|
537
|
+
# the update loop on the stack. @api private
|
|
448
538
|
def deliver
|
|
449
539
|
i = 0
|
|
450
540
|
while i < @handled.size
|
|
@@ -463,6 +553,7 @@ module Asterism
|
|
|
463
553
|
end
|
|
464
554
|
end
|
|
465
555
|
|
|
556
|
+
# @api private
|
|
466
557
|
def fire_timers
|
|
467
558
|
return if @timers.empty?
|
|
468
559
|
now = ::Asterism::ROS.now_ms
|
|
@@ -480,7 +571,7 @@ module Asterism
|
|
|
480
571
|
end
|
|
481
572
|
|
|
482
573
|
# One message for one subscribe block, one timer firing (CRuby routes
|
|
483
|
-
# what they raise to on_error).
|
|
574
|
+
# what they raise to on_error). @api private
|
|
484
575
|
def handle(sub, msg, info)
|
|
485
576
|
sub.handler.call(msg, info)
|
|
486
577
|
end
|
|
@@ -508,6 +599,7 @@ module Asterism
|
|
|
508
599
|
end
|
|
509
600
|
|
|
510
601
|
# For the entities: [data key, liveliness token key] of a topic.
|
|
602
|
+
# @api private
|
|
511
603
|
def entity_keys(kind, topic, type, qos)
|
|
512
604
|
full = ::Asterism::ROS.resolve(topic, @namespace)
|
|
513
605
|
data = "#{@domain}/#{::Asterism::ROS.strip_slashes(full)}/#{type::TYPE_NAME}/#{type::TYPE_HASH}"
|
|
@@ -535,7 +627,7 @@ module Asterism
|
|
|
535
627
|
# msg: a message of the type, or a Hash of its fields (missing ones
|
|
536
628
|
# take their defaults).
|
|
537
629
|
def publish(msg)
|
|
538
|
-
raise ::Asterism::Zenoh::
|
|
630
|
+
raise ::Asterism::Zenoh::ClosedError, "publisher closed" if @token.nil?
|
|
539
631
|
# Read once (a second thread may publish meanwhile on CRuby).
|
|
540
632
|
seq = @sequence + 1
|
|
541
633
|
@sequence = seq
|
|
@@ -566,7 +658,7 @@ module Asterism
|
|
|
566
658
|
@type = type
|
|
567
659
|
@topic_key, @token_key = node.entity_keys("MS", topic, type, qos)
|
|
568
660
|
@errors = 0
|
|
569
|
-
@sub = session.subscribe(@topic_key, depth)
|
|
661
|
+
@sub = session.subscribe(@topic_key, depth: depth)
|
|
570
662
|
@token = session.liveliness(@token_key)
|
|
571
663
|
end
|
|
572
664
|
|
|
@@ -591,6 +683,16 @@ module Asterism
|
|
|
591
683
|
out.size
|
|
592
684
|
end
|
|
593
685
|
|
|
686
|
+
# Messages waiting / received so far / dropped because the queue was
|
|
687
|
+
# full (the counters of the Zenoh subscriber underneath).
|
|
688
|
+
def pending
|
|
689
|
+
@sub.pending
|
|
690
|
+
end
|
|
691
|
+
|
|
692
|
+
def received
|
|
693
|
+
@sub.received
|
|
694
|
+
end
|
|
695
|
+
|
|
594
696
|
def dropped
|
|
595
697
|
@sub.dropped
|
|
596
698
|
end
|
|
@@ -609,14 +711,16 @@ module Asterism
|
|
|
609
711
|
|
|
610
712
|
# node.every: a block called every period, from node.poll.
|
|
611
713
|
class Timer
|
|
612
|
-
# period: seconds as given;
|
|
613
|
-
|
|
714
|
+
# period: seconds (as given, or ms / 1000.0); period_ms: the same in
|
|
715
|
+
# milliseconds; fired: how many times it ran.
|
|
716
|
+
attr_reader :period, :period_ms, :fired
|
|
614
717
|
|
|
615
|
-
|
|
616
|
-
|
|
718
|
+
# @api private (node.every makes it)
|
|
719
|
+
def initialize(node, seconds, blk, ms = nil)
|
|
720
|
+
@period_ms = ms.nil? ? (seconds * 1000).round : ms.round
|
|
617
721
|
raise ArgumentError, "the period must be positive" unless @period_ms > 0
|
|
618
722
|
@node = node
|
|
619
|
-
@period = seconds
|
|
723
|
+
@period = ms.nil? ? seconds : ms / 1000.0
|
|
620
724
|
@blk = blk
|
|
621
725
|
@next = ::Asterism::ROS.now_ms + @period_ms
|
|
622
726
|
@fired = 0
|
|
@@ -659,7 +763,7 @@ module Asterism
|
|
|
659
763
|
@service_key, @token_key = node.entity_keys("SS", service, type, qos)
|
|
660
764
|
@handled = 0
|
|
661
765
|
@errors = 0
|
|
662
|
-
@queryable = session.queryable(@service_key, depth, complete: true)
|
|
766
|
+
@queryable = session.queryable(@service_key, depth: depth, complete: true)
|
|
663
767
|
@token = session.liveliness(@token_key)
|
|
664
768
|
end
|
|
665
769
|
|
|
@@ -739,6 +843,11 @@ module Asterism
|
|
|
739
843
|
@nobody = false
|
|
740
844
|
end
|
|
741
845
|
|
|
846
|
+
# took_ms in seconds (a Float; nil until done).
|
|
847
|
+
def took
|
|
848
|
+
@took_ms.nil? ? nil : @took_ms / 1000.0
|
|
849
|
+
end
|
|
850
|
+
|
|
742
851
|
# True once the response came, the time ran out, or no server answered.
|
|
743
852
|
def done?
|
|
744
853
|
collect
|
|
@@ -746,19 +855,23 @@ module Asterism
|
|
|
746
855
|
end
|
|
747
856
|
|
|
748
857
|
# The response (type::Response); waits until done. Raises
|
|
749
|
-
# Asterism::ROS::
|
|
858
|
+
# Asterism::ROS::TimeoutError when there was none, and
|
|
859
|
+
# Asterism::Zenoh::ClosedError when the session closed (1.0:
|
|
860
|
+
# Asterism::Disconnected).
|
|
750
861
|
def value
|
|
751
862
|
@client.wait_for(self) unless done?
|
|
752
863
|
if @response.nil?
|
|
753
|
-
raise ::Asterism::Zenoh::
|
|
864
|
+
raise ::Asterism::Zenoh::ClosedError, "session is closed" if @client.session_closed?
|
|
754
865
|
if @nobody
|
|
755
|
-
raise ::Asterism::ROS::
|
|
866
|
+
raise ::Asterism::ROS::TimeoutError, "no answer from #{@client.service_name} (nobody serves it)"
|
|
756
867
|
end
|
|
757
|
-
raise ::Asterism::ROS::
|
|
868
|
+
raise ::Asterism::ROS::TimeoutError,
|
|
869
|
+
"no answer from #{@client.service_name} within #{@timeout_ms / 1000.0} s (#{@timeout_ms} ms)"
|
|
758
870
|
end
|
|
759
871
|
@response
|
|
760
872
|
end
|
|
761
873
|
|
|
874
|
+
# @api private
|
|
762
875
|
def collect
|
|
763
876
|
return if @finished
|
|
764
877
|
g = @get
|
|
@@ -788,6 +901,8 @@ module Asterism
|
|
|
788
901
|
# The client side of a service (node.client). Sends each request as a
|
|
789
902
|
# get on the service key, plus the SC token.
|
|
790
903
|
class Client
|
|
904
|
+
# The time limit of a call, in seconds and in milliseconds.
|
|
905
|
+
DEFAULT_TIMEOUT = 2.0
|
|
791
906
|
DEFAULT_TIMEOUT_MS = 2000
|
|
792
907
|
# Pause between polls while a call waits (ms).
|
|
793
908
|
WAIT_STEP_MS = 2
|
|
@@ -805,11 +920,14 @@ module Asterism
|
|
|
805
920
|
@token = session.liveliness(@token_key)
|
|
806
921
|
end
|
|
807
922
|
|
|
808
|
-
# Sends the request and returns a Call at once. request: a
|
|
809
|
-
# type::Request
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
923
|
+
# Sends the request and returns a Call at once. The request: a
|
|
924
|
+
# type::Request or a Hash (positional, or request:), or the fields as
|
|
925
|
+
# keywords. The time limit: timeout: (seconds) or timeout_ms: (2 s).
|
|
926
|
+
# A request with a field named timeout or timeout_ms is best given as
|
|
927
|
+
# request: { ... } (see README, Reserved keywords).
|
|
928
|
+
def call_async(req = nil, request: nil, timeout: nil, timeout_ms: nil, **fields)
|
|
929
|
+
raise ::Asterism::Zenoh::ClosedError, "client closed" if @token.nil?
|
|
930
|
+
req, timeout_ms = request_and_limit("Client#call", req, request, timeout, timeout_ms, fields)
|
|
813
931
|
payload = @type::Request.encode(req)
|
|
814
932
|
# Read once into a local: the request and its Call carry the same
|
|
815
933
|
# number even when another thread (CRuby) calls through this client
|
|
@@ -817,20 +935,47 @@ module Asterism
|
|
|
817
935
|
seq = @sequence + 1
|
|
818
936
|
@sequence = seq
|
|
819
937
|
att = ::Asterism::ROS::Attachment.new(seq, ::Asterism::ROS.now_ns, @gid)
|
|
820
|
-
g = @session.get(@service_key, timeout_ms
|
|
938
|
+
g = @session.get(@service_key, timeout_ms: timeout_ms, payload: payload, attachment: att.encode,
|
|
821
939
|
target: :all_complete, consolidation: :none)
|
|
822
940
|
::Asterism::ROS::Call.new(self, g, seq, timeout_ms)
|
|
823
941
|
end
|
|
824
942
|
|
|
825
943
|
# Sends the request and waits for the response (polling the node, so
|
|
826
944
|
# this node's services keep answering meanwhile). Raises
|
|
827
|
-
# Asterism::ROS::
|
|
828
|
-
def call(
|
|
829
|
-
call_async(request, timeout_ms: timeout_ms, **fields).value
|
|
945
|
+
# Asterism::ROS::TimeoutError when no response came in time.
|
|
946
|
+
def call(req = nil, request: nil, timeout: nil, timeout_ms: nil, **fields)
|
|
947
|
+
call_async(req, request: request, timeout: timeout, timeout_ms: timeout_ms, **fields).value
|
|
948
|
+
end
|
|
949
|
+
|
|
950
|
+
# The request and the time limit (ms) from call's arguments. A
|
|
951
|
+
# keyword that is also a field of the request type is ambiguous: a
|
|
952
|
+
# field named timeout stays a field (as before 0.4.0), timeout_ms
|
|
953
|
+
# stays the time limit, and both warn (request: { ... } says which).
|
|
954
|
+
# @api private
|
|
955
|
+
def request_and_limit(where, req, request, timeout, timeout_ms, fields)
|
|
956
|
+
raise ArgumentError, "#{where}: give the request once (positional or request:)" if !req.nil? && !request.nil?
|
|
957
|
+
req = request if req.nil?
|
|
958
|
+
if req.nil?
|
|
959
|
+
names = @type::Request::FIELDS
|
|
960
|
+
if !timeout.nil? && names.include?(:timeout)
|
|
961
|
+
::Asterism.deprecated("#{where} with a request field named timeout as a keyword",
|
|
962
|
+
"request: { timeout: ... } (timeout: is the time limit in seconds)",
|
|
963
|
+
"it is taken as the field here, as before 0.4.0")
|
|
964
|
+
fields[:timeout] = timeout
|
|
965
|
+
timeout = nil
|
|
966
|
+
end
|
|
967
|
+
if !timeout_ms.nil? && names.include?(:timeout_ms)
|
|
968
|
+
::Asterism.deprecated("#{where} with a request field named timeout_ms as a keyword",
|
|
969
|
+
"request: { timeout_ms: ... } and timeout: for the time limit",
|
|
970
|
+
"timeout_ms: is taken as the time limit here")
|
|
971
|
+
end
|
|
972
|
+
req = fields
|
|
973
|
+
end
|
|
974
|
+
[req, ::Asterism.time_ms(where, timeout, timeout_ms, nil, DEFAULT_TIMEOUT_MS)]
|
|
830
975
|
end
|
|
831
976
|
|
|
832
977
|
# Polls the node's session and services (not its subscribe / every
|
|
833
|
-
# blocks) until the call is done.
|
|
978
|
+
# blocks) until the call is done. @api private
|
|
834
979
|
def wait_for(c)
|
|
835
980
|
until c.done?
|
|
836
981
|
break unless @node.pump
|
data/tools/asterism_msggen.rb
CHANGED
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
#
|
|
12
12
|
# Each generated file defines one type (a message, or a service with its
|
|
13
13
|
# Request and Response) under Asterism::ROS::<PackageInCamelCase>, with its
|
|
14
|
-
# fields and defaults,
|
|
15
|
-
# TYPE_HASH (RIHS01, computed here)
|
|
16
|
-
# on top of Asterism::CDR. A file
|
|
17
|
-
#
|
|
18
|
-
# <out>/<pkg>/msg/<Name>.rb and
|
|
14
|
+
# fields and defaults, FIELD_TYPES (what each field is), ROS_NAME,
|
|
15
|
+
# TYPE_NAME (the DDS name rmw_zenoh uses), TYPE_HASH (RIHS01, computed here)
|
|
16
|
+
# and the CDR conversion in both directions on top of Asterism::CDR. A file
|
|
17
|
+
# loads the types it is made of through Asterism::ROS.require_type first.
|
|
18
|
+
# The output layout mirrors the ROS names: <out>/<pkg>/msg/<Name>.rb and
|
|
19
|
+
# <out>/<pkg>/srv/<Name>.rb.
|
|
19
20
|
#
|
|
20
21
|
# The type hash follows the type description rules of ROS 2 Jazzy
|
|
21
22
|
# (rosidl_generator_type_description, REP 2011): the type and every type it
|
|
@@ -578,6 +579,7 @@ module AsterismMsgGen
|
|
|
578
579
|
out << " TYPE_NAME = #{dds_name(m).inspect}\n"
|
|
579
580
|
out << " TYPE_HASH = #{hash.inspect}\n"
|
|
580
581
|
out << " FIELDS = [#{fs.map { |f| ":#{f.name}" }.join(', ')}]\n"
|
|
582
|
+
out << field_types_lit(fs)
|
|
581
583
|
m.constants.each do |c|
|
|
582
584
|
out << " #{c.name} = #{lit(c.value, c.base)}\n"
|
|
583
585
|
end
|
|
@@ -616,6 +618,31 @@ module AsterismMsgGen
|
|
|
616
618
|
PRIMITIVES.fetch(f.base)[1]
|
|
617
619
|
end
|
|
618
620
|
|
|
621
|
+
# What a field is, for tools that look at a type without its .msg (the
|
|
622
|
+
# same plain values on mruby and CRuby: Arrays, Strings, Integers, nil):
|
|
623
|
+
# [name, base, kind, nested, capacity, string_capacity]
|
|
624
|
+
# base: the .msg type ("float64", "string", "uint8", ...) or, for a
|
|
625
|
+
# message, its full name; kind: "scalar", "array" (T[N]),
|
|
626
|
+
# "bounded_sequence" (T[<=N]) or "sequence" (T[]); nested: the full name
|
|
627
|
+
# of the message type, or nil; capacity: N of an array or a bounded
|
|
628
|
+
# sequence, else nil; string_capacity: N of string<=N, else nil.
|
|
629
|
+
FIELD_KINDS = { nil => "scalar", fixed: "array", bounded: "bounded_sequence", unbounded: "sequence" }.freeze
|
|
630
|
+
|
|
631
|
+
def self.field_type(f)
|
|
632
|
+
cap = f.array == :fixed || f.array == :bounded ? f.size : nil
|
|
633
|
+
[f.name, f.base, FIELD_KINDS.fetch(f.array), f.nested, cap, f.string_max]
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
def field_types_lit(fs)
|
|
637
|
+
return " FIELD_TYPES = []\n" if fs.empty?
|
|
638
|
+
out = +" FIELD_TYPES = [\n"
|
|
639
|
+
fs.each_with_index do |f, i|
|
|
640
|
+
items = Emitter.field_type(f).map { |v| v.nil? ? "nil" : v.inspect }
|
|
641
|
+
out << " [#{items.join(', ')}]#{i < fs.size - 1 ? ',' : ''}\n"
|
|
642
|
+
end
|
|
643
|
+
out << " ]\n"
|
|
644
|
+
end
|
|
645
|
+
|
|
619
646
|
def byte_kind?(f)
|
|
620
647
|
BYTE_KINDS.include?(f.base)
|
|
621
648
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: asterism
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Katsuhiko Kageyama
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: asterism-zenoh
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.
|
|
19
|
+
version: 0.4.0
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 0.
|
|
26
|
+
version: 0.4.0
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: msgpack
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -46,13 +46,18 @@ executables: []
|
|
|
46
46
|
extensions: []
|
|
47
47
|
extra_rdoc_files: []
|
|
48
48
|
files:
|
|
49
|
+
- CHANGELOG.md
|
|
49
50
|
- LICENSE
|
|
50
51
|
- NOTICE
|
|
51
52
|
- README.md
|
|
52
53
|
- data/msgs/LICENSE-Apache-2.0.txt
|
|
54
|
+
- data/msgs/LICENSE-BSD-3-Clause-tf2_msgs.txt
|
|
53
55
|
- data/msgs/NOTICE
|
|
54
56
|
- data/msgs/builtin_interfaces/msg/Duration.rb
|
|
55
57
|
- data/msgs/builtin_interfaces/msg/Time.rb
|
|
58
|
+
- data/msgs/diagnostic_msgs/msg/DiagnosticArray.rb
|
|
59
|
+
- data/msgs/diagnostic_msgs/msg/DiagnosticStatus.rb
|
|
60
|
+
- data/msgs/diagnostic_msgs/msg/KeyValue.rb
|
|
56
61
|
- data/msgs/example_interfaces/srv/AddTwoInts.rb
|
|
57
62
|
- data/msgs/geometry_msgs/msg/Accel.rb
|
|
58
63
|
- data/msgs/geometry_msgs/msg/AccelStamped.rb
|
|
@@ -62,24 +67,38 @@ files:
|
|
|
62
67
|
- data/msgs/geometry_msgs/msg/Pose.rb
|
|
63
68
|
- data/msgs/geometry_msgs/msg/Pose2D.rb
|
|
64
69
|
- data/msgs/geometry_msgs/msg/PoseStamped.rb
|
|
70
|
+
- data/msgs/geometry_msgs/msg/PoseWithCovariance.rb
|
|
65
71
|
- data/msgs/geometry_msgs/msg/Quaternion.rb
|
|
66
72
|
- data/msgs/geometry_msgs/msg/QuaternionStamped.rb
|
|
67
73
|
- data/msgs/geometry_msgs/msg/Transform.rb
|
|
68
74
|
- data/msgs/geometry_msgs/msg/TransformStamped.rb
|
|
69
75
|
- data/msgs/geometry_msgs/msg/Twist.rb
|
|
70
76
|
- data/msgs/geometry_msgs/msg/TwistStamped.rb
|
|
77
|
+
- data/msgs/geometry_msgs/msg/TwistWithCovariance.rb
|
|
71
78
|
- data/msgs/geometry_msgs/msg/Vector3.rb
|
|
72
79
|
- data/msgs/geometry_msgs/msg/Vector3Stamped.rb
|
|
73
80
|
- data/msgs/geometry_msgs/msg/Wrench.rb
|
|
74
81
|
- data/msgs/geometry_msgs/msg/WrenchStamped.rb
|
|
82
|
+
- data/msgs/nav_msgs/msg/MapMetaData.rb
|
|
83
|
+
- data/msgs/nav_msgs/msg/OccupancyGrid.rb
|
|
84
|
+
- data/msgs/nav_msgs/msg/Odometry.rb
|
|
85
|
+
- data/msgs/nav_msgs/msg/Path.rb
|
|
86
|
+
- data/msgs/rcl_interfaces/msg/Log.rb
|
|
87
|
+
- data/msgs/rcl_interfaces/msg/Parameter.rb
|
|
88
|
+
- data/msgs/rcl_interfaces/msg/ParameterEvent.rb
|
|
89
|
+
- data/msgs/rcl_interfaces/msg/ParameterValue.rb
|
|
75
90
|
- data/msgs/sensor_msgs/msg/BatteryState.rb
|
|
91
|
+
- data/msgs/sensor_msgs/msg/CompressedImage.rb
|
|
76
92
|
- data/msgs/sensor_msgs/msg/FluidPressure.rb
|
|
77
93
|
- data/msgs/sensor_msgs/msg/Illuminance.rb
|
|
78
94
|
- data/msgs/sensor_msgs/msg/Imu.rb
|
|
79
95
|
- data/msgs/sensor_msgs/msg/JointState.rb
|
|
96
|
+
- data/msgs/sensor_msgs/msg/LaserScan.rb
|
|
80
97
|
- data/msgs/sensor_msgs/msg/MagneticField.rb
|
|
81
98
|
- data/msgs/sensor_msgs/msg/NavSatFix.rb
|
|
82
99
|
- data/msgs/sensor_msgs/msg/NavSatStatus.rb
|
|
100
|
+
- data/msgs/sensor_msgs/msg/PointCloud2.rb
|
|
101
|
+
- data/msgs/sensor_msgs/msg/PointField.rb
|
|
83
102
|
- data/msgs/sensor_msgs/msg/Range.rb
|
|
84
103
|
- data/msgs/sensor_msgs/msg/RelativeHumidity.rb
|
|
85
104
|
- data/msgs/sensor_msgs/msg/Temperature.rb
|
|
@@ -113,6 +132,11 @@ files:
|
|
|
113
132
|
- data/msgs/std_msgs/msg/UInt64MultiArray.rb
|
|
114
133
|
- data/msgs/std_msgs/msg/UInt8.rb
|
|
115
134
|
- data/msgs/std_msgs/msg/UInt8MultiArray.rb
|
|
135
|
+
- data/msgs/tf2_msgs/msg/TFMessage.rb
|
|
136
|
+
- data/msgs/visualization_msgs/msg/Marker.rb
|
|
137
|
+
- data/msgs/visualization_msgs/msg/MarkerArray.rb
|
|
138
|
+
- data/msgs/visualization_msgs/msg/MeshFile.rb
|
|
139
|
+
- data/msgs/visualization_msgs/msg/UVCoordinate.rb
|
|
116
140
|
- lib/asterism.rb
|
|
117
141
|
- lib/asterism/cruby.rb
|
|
118
142
|
- lib/asterism/cruby/objects.rb
|
|
@@ -130,6 +154,7 @@ homepage: https://github.com/ruby-asterism/asterism
|
|
|
130
154
|
licenses:
|
|
131
155
|
- MIT
|
|
132
156
|
- Apache-2.0
|
|
157
|
+
- BSD-3-Clause
|
|
133
158
|
metadata:
|
|
134
159
|
homepage_uri: https://github.com/ruby-asterism/asterism
|
|
135
160
|
source_code_uri: https://github.com/ruby-asterism/asterism
|