gz-transport
gz-transport copied to clipboard
Add overloaded Request for non-blocking calls using abstract types
🎉 New feature
Summary
Add an overloaded Request method to the class Node that accepts references to abstract request and response types, specifically the type google::protobuf::Message
.
The addition is to support a non-blocking version of the service call used in cmdServiceReq
(gz-transport/src/cmd/gz.hh). The existing Request methods accepting a callback function all require a concrete type as they call Request().GetTypeName()
and Response().GetTypeName()
. This does not work when the request or response types are the abstract base class google::protobuf::Message
. The instance calls _request->GetTypeName()
and _response->GetTypeName()
on the other hand are valid, and this is what the new overload function uses.
There is also a change to the template specialisation of ReqHandler
to support callbacks.
Test it
Tasks
- [ ] Change target to
main
because of ABI changes. - [ ] Add a test to
gz-transport
The current use case is in a downstream project providing python bindings for gz-msgs and gz-transport: https://github.com/srmainwaring/gz-python/tree/srmainwaring/service-request
At present only blocking service requests are supported. The Python bindings use the same approach as the gz service
command line tools. This PR allows asynchronous callbacks as well. The example below is for a service that allows the PID settings of a joint controller to be updated in Python at runtime. Both blocking and non-blocking versions of the call are demonstrated.
import time
from gz.msgs.pid_pb2 import PID
from gz.transport import Node
# service response callback
def rep_cb(msg, result):
print("Result: {}".format(result))
print("Response: {}".format(msg))
def main():
# create a transport node
node = Node()
service = "/model/iris_with_ardupilot/"\
"joint/iris_with_standoffs::rotor_0_joint/pid"
req_type_name = PID.DESCRIPTOR.full_name
rep_type_name = PID.DESCRIPTOR.full_name
# populate PID message
req = PID()
req.p_gain_optional.data = 0.2
req.i_gain_optional.data = 0.0
req.d_gain_optional.data = 0.0
req.i_max_optional.data = 1.0
req.i_min_optional.data = -1.0
req.limit_optional.data = 10.0
# timeout in ms
timeout = 1000
# call service (blocking)
print("Blocking service call")
executed = node.request(service, req, timeout, rep_type_name)
# update PID so we can verify second call
req.p_gain_optional.data = 0.3
# call service (non-blocking)
print("Non-blocking service call")
executed = node.request(service, req, rep_cb, rep_type_name)
# wait for response before exiting
time.sleep(timeout/1000)
print("Done")
if __name__ == "__main__":
main()
Expected console output:
% ~/Code/osrf/gz_garden_ws/src/gz-python/python/pid_service.py
Blocking service call
Non-blocking service call
Result: True
Response: p_gain_optional {
data: 0.3
}
i_gain_optional {
}
d_gain_optional {
}
i_max_optional {
data: 1.0
}
i_min_optional {
data: -1.0
}
limit_optional {
data: 10.0
}
Done
Expected gz sim
output (using a modified version of the ArduPilot plugin: https://github.com/srmainwaring/ardupilot_gazebo-1/commit/c938fd5922699b961ab2aa7fbb8f93743c670c15):
[Msg] Updated PIDs for [iris_with_standoffs::rotor_0_joint]
p_gain: 0.2
i_gain: 0
d_gain: 0
i_max: 1
i_min: -1
cmd_max: 10
cmd_min: -10
[Msg] Updated PIDs for [iris_with_standoffs::rotor_0_joint]
p_gain: 0.3
i_gain: 0
d_gain: 0
i_max: 1
i_min: -1
cmd_max: 10
cmd_min: -10
Checklist
- [x] Signed all commits for DCO
- [ ] Added tests
- [ ] Added example and/or tutorial
- [ ] Updated documentation (as needed)
- [ ] Updated migration guide (as needed)
- [ ] Consider updating Python bindings (if the library has them)
- [ ]
codecheck
passed (See contributing) - [ ] All tests passed (See test coverage)
- [ ] While waiting for a review on your PR, please help review another open pull request to support the maintainers
Note to maintainers: Remember to use Squash-Merge and edit the commit message to match the pull request summary while retaining Signed-off-by
messages.