Skip to content

Commit

Permalink
Merge pull request #68 from schmrlng/pycall_api_update
Browse files Browse the repository at this point in the history
Changes for PyCall API update getindex -> getproperty; fix #67
  • Loading branch information
jdlangs committed May 14, 2019
2 parents 33a8496 + b4ca604 commit 7fd1aee
Show file tree
Hide file tree
Showing 12 changed files with 100 additions and 86 deletions.
12 changes: 8 additions & 4 deletions .travis.yml
Expand Up @@ -2,22 +2,26 @@ language: julia
julia:
- 0.7
- 1.0
- 1.1
- nightly
matrix:
allow_failures:
- julia: nightly
sudo: required
dist: trusty
dist: xenial
env:
global:
- PYTHON=python
before_install:
- sudo apt-add-repository -y "deb http://packages.ros.org/ros/ubuntu trusty main"
- sudo apt-add-repository -y "deb http://packages.ros.org/ros/ubuntu xenial main"
- wget https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -O - | sudo apt-key add -
- sudo apt-get update
- sudo apt-get -y install ros-indigo-ros-base ros-indigo-common-msgs
- sudo apt-get -y install ros-kinetic-ros-base ros-kinetic-common-msgs
- sudo rosdep init
- rosdep update
before_script:
- export PATH=/usr/bin:$PATH
- source /opt/ros/indigo/setup.sh
- source /opt/ros/kinetic/setup.sh
- roscore &
- sleep 5
- python test/echonode.py &
Expand Down
2 changes: 1 addition & 1 deletion REQUIRE
@@ -1,2 +1,2 @@
julia 0.7
PyCall 1.17.1
PyCall 1.90.0
4 changes: 2 additions & 2 deletions docs/src/index.md
Expand Up @@ -175,12 +175,12 @@ if `myproxy` is a `ServiceProxy` object, it can be called with
in the `RobotOS` module with the same syntax as in rospy.

### Message Constants
Message constants may be accessed using `getindex` syntax. For example for
Message constants may be accessed using `getproperty` syntax. For example for
[visualization_msgs/Marker.msg](http://docs.ros.org/api/visualization_msgs/html/msg/Marker.html)
we have:

import .visualization_msgs.msg: Marker
Marker[:SPHERE] == getindex(Marker, :SPHERE) == 2 # true
Marker.SPHERE == getproperty(Marker, :SPHERE) == 2 # true

## ROS Integration

Expand Down
6 changes: 3 additions & 3 deletions src/RobotOS.jl
Expand Up @@ -18,11 +18,11 @@ include("callbacks.jl")
function __init__()
#Put julia's ARGS into python's so remappings will work
copy!(_py_sys, pyimport("sys"))
_py_sys["argv"] = ARGS
_py_sys."argv" = ARGS

#Fill in empty PyObjects
if ! (dirname(@__FILE__) in _py_sys["path"])
pushfirst!(_py_sys["path"], dirname(@__FILE__))
if ! (dirname(@__FILE__) in _py_sys."path")
pushfirst!(_py_sys."path", dirname(@__FILE__))
end
copy!(_py_ros_callbacks, pyimport("ros_callbacks"))

Expand Down
8 changes: 4 additions & 4 deletions src/callbacks.jl
Expand Up @@ -18,18 +18,18 @@ function _callback_async_loop(rosobj, cond)
end

function _run_callbacks(sub::Subscriber{M}) where M
while pycall(sub.queue["size"], PyAny) > 0
msg = pycall(sub.queue["get"], PyObject)
while pycall(sub.queue."size", PyAny) > 0
msg = pycall(sub.queue."get", PyObject)
sub.callback(convert(M, msg), sub.callback_args...)
end
end

function _run_callbacks(srv::Service{T}) where T
ReqType = _srv_reqtype(T)

req = pycall(srv.cb_interface["get_request"], PyObject)
req = pycall(srv.cb_interface."get_request", PyObject)
response = srv.handler(convert(ReqType, req))

#Python callback is blocking until the response is ready
pycall(srv.cb_interface["set_response"], PyAny, convert(PyObject, response))
pycall(srv.cb_interface."set_response", PyAny, convert(PyObject, response))
end
58 changes: 34 additions & 24 deletions src/gentypes.jl
Expand Up @@ -183,7 +183,7 @@ function addtype!(mod::ROSMsgModule, typ::String)
end
pymod, pyobj = _pyvars(_fullname(mod), typ)

deptypes = pyobj[:_slot_types]
deptypes = pyobj._slot_types
_importdeps!(mod, deptypes)

push!(mod.members, typ)
Expand All @@ -199,15 +199,15 @@ function addtype!(mod::ROSSrvModule, typ::String)
@debug("Service type import: ", _fullname(mod), ".", typ)
pymod, pyobj = _pyvars(_fullname(mod), typ)

if ! haskey(pyobj, "_request_class")
if ! PyCall.hasproperty(pyobj, "_request_class")
error(string("Incorrect service name: ", typ))
end

#Immediately import dependencies from the Request/Response classes
#Repeats are OK
req_obj = pymod[string(typ,"Request")]
resp_obj = pymod[string(typ,"Response")]
deptypes = [req_obj[:_slot_types]; resp_obj[:_slot_types]]
req_obj = getproperty(pymod, string(typ,"Request"))
resp_obj = getproperty(pymod, string(typ,"Response"))
deptypes = [req_obj._slot_types; resp_obj._slot_types]
_importdeps!(mod, deptypes)

push!(mod.members, typ)
Expand All @@ -222,7 +222,7 @@ end
function _pyvars(modname::String, typ::String)
pymod = _import_rospy_pkg(modname)
pyobj =
try pymod[typ]
try getproperty(pymod, typ)
catch ex
isa(ex, KeyError) || rethrow(ex)
error("Message type '$typ' not found in ROS package '$modname', ",
Expand Down Expand Up @@ -269,7 +269,7 @@ function _import_rospy_pkg(package::String)
_rospy_modules[package] = pyimport(package)
catch ex
show(ex)
error("python import error: $(ex.val[:args][1])")
error("python import error: $(ex.val.args[1])")
end
end
_rospy_modules[package]
Expand Down Expand Up @@ -324,7 +324,7 @@ function modulecode(mod::ROSModule, rosrootmod::Module)
push!(modcode,
quote
using PyCall
import Base: convert, getindex
import Base: convert, getproperty
import RobotOS
import RobotOS.Time
import RobotOS.Duration
Expand Down Expand Up @@ -390,8 +390,8 @@ function buildtype(mod::ROSMsgModule, typename::String)
global _rospy_objects
fulltypestr = _rostypestr(mod, typename)
pyobj = _rospy_objects[fulltypestr]
memnames = pyobj[:__slots__]
memtypes = pyobj[:_slot_types]
memnames = pyobj.__slots__
memtypes = pyobj._slot_types
members = collect(zip(memnames, memtypes))

typecode(fulltypestr, :AbstractMsg, members)
Expand All @@ -404,16 +404,16 @@ function buildtype(mod::ROSSrvModule, typename::String)

req_typestr = _rostypestr(mod, string(typename,"Request"))
reqobj = _rospy_objects[req_typestr]
memnames = reqobj[:__slots__]
memtypes = reqobj[:_slot_types]
memnames = reqobj.__slots__
memtypes = reqobj._slot_types
reqmems = collect(zip(memnames, memtypes))
pyreq = :(RobotOS._rospy_objects[$req_typestr])
reqexprs = typecode(req_typestr, :AbstractSrv, reqmems)

resp_typestr = _rostypestr(mod, string(typename,"Response"))
respobj = _rospy_objects[resp_typestr]
memnames = respobj[:__slots__]
memtypes = respobj[:_slot_types]
memnames = respobj.__slots__
memtypes = respobj._slot_types
respmems = collect(zip(memnames, memtypes))
pyresp = :(RobotOS._rospy_objects[$resp_typestr])
respexprs = typecode(resp_typestr, :AbstractSrv, respmems)
Expand All @@ -436,7 +436,7 @@ end
# (2) Default outer constructer with no arguments
# (3) convert(PyObject, ...)
# (4) convert(..., o::PyObject)
# (5) getindex for accessing member constants
# (5) getproperty for accessing member constants
function typecode(rosname::String, super::Symbol, members::Vector)
tname = _splittypestr(rosname)[2]
@debug("Type: ", tname)
Expand Down Expand Up @@ -477,17 +477,27 @@ function typecode(rosname::String, super::Symbol, members::Vector)
#(4) Convert from PyObject
push!(exprs, :(
function convert(jlt::Type{$jlsym}, o::PyObject)
if convert(String, o["_type"]) != _typerepr(jlt)
if convert(String, o."_type") != _typerepr(jlt)
throw(InexactError(:convert, $jlsym, o))
end
jl = $jlsym()
#Generated code here
jl
end
))
#(5) Accessing member variables through getindex
#(5) Accessing member variables through getproperty
push!(exprs, :(
getindex(::Type{$jlsym}, s::Symbol) = RobotOS._rospy_objects[$rosname][s]
function getproperty(::Type{$jlsym}, s::Symbol)
try getproperty(RobotOS._rospy_objects[$rosname], s)
catch ex
isa(ex, KeyError) || rethrow(ex)
try getfield($jlsym, s)
catch ex2
startswith(ex2.msg, "type DataType has no field") || rethrow(ex2)
error("Message type '" * $("$jlsym") * "' has no property '$s'.")
end
end
end
))

#Now add the meat to the empty expressions above
Expand Down Expand Up @@ -534,25 +544,25 @@ function _addtypemember!(exprs, namestr, typestr)
if arraylen >= 0
memexpr = :($namesym::Array{$j_typ,1})
defexpr = :([$j_def for i = 1:$arraylen])
jlconexpr = :(jl.$namesym = convert(Array{$j_typ,1}, o[$namestr]))
jlconexpr = :(jl.$namesym = convert(Array{$j_typ,1}, o.$namestr))

#uint8[] is string in rospy and PyCall's conversion to bytearray is
#rejected by ROS
if j_typ == :UInt8
pyconexpr = :(py[$namestr] =
pyconexpr = :(py.$namestr =
pycall(pybuiltin("str"), PyObject, PyObject(o.$namesym))
)
elseif _isrostype(typestr)
pyconexpr = :(py[$namestr] =
pyconexpr = :(py.$namestr =
convert(Array{PyObject,1}, o.$namesym))
else
pyconexpr = :(py[$namestr] = o.$namesym)
pyconexpr = :(py.$namestr = o.$namesym)
end
else
memexpr = :($namesym::$j_typ)
defexpr = j_def
jlconexpr = :(jl.$namesym = convert($j_typ, o[$namestr]))
pyconexpr = :(py[$namestr] = convert(PyObject, o.$namesym))
jlconexpr = :(jl.$namesym = convert($j_typ, o.$namestr))
pyconexpr = :(py.$namestr = convert(PyObject, o.$namesym))
end
push!(typeargs, memexpr)
insert!(jlconargs, length(jlconargs), jlconexpr)
Expand Down
8 changes: 4 additions & 4 deletions src/pubsub.jl
Expand Up @@ -14,7 +14,7 @@ struct Publisher{MsgType<:AbstractMsg}
function Publisher{MT}(topic::AbstractString; kwargs...) where MT <: AbstractMsg
@debug("Creating <$(string(MT))> publisher on topic: '$topic'")
rospycls = _get_rospy_class(MT)
return new{MT}(__rospy__[:Publisher](ascii(topic), rospycls; kwargs...))
return new{MT}(__rospy__.Publisher(ascii(topic), rospycls; kwargs...))
end
end

Expand All @@ -27,7 +27,7 @@ Publisher(topic::AbstractString, ::Type{MT}; kwargs...) where {MT <: AbstractMsg
Publish `msg` on `p`, a `Publisher` with matching message type.
"""
function publish(p::Publisher{MT}, msg::MT) where MT <: AbstractMsg
pycall(p.o["publish"], PyAny, convert(PyObject, msg))
pycall(p.o."publish", PyAny, convert(PyObject, msg))
end

"""
Expand All @@ -52,8 +52,8 @@ mutable struct Subscriber{MsgType<:AbstractMsg}
rospycls = _get_rospy_class(MT)

cond = Base.AsyncCondition()
mqueue = _py_ros_callbacks["MessageQueue"](CB_NOTIFY_PTR[], cond.handle)
subobj = __rospy__[:Subscriber](ascii(topic), rospycls, mqueue["storemsg"]; kwargs...)
mqueue = _py_ros_callbacks."MessageQueue"(CB_NOTIFY_PTR[], cond.handle)
subobj = __rospy__.Subscriber(ascii(topic), rospycls, mqueue."storemsg"; kwargs...)

rosobj = new{MT}(cb, cb_args, subobj, mqueue)
cbloop = Task(() -> _callback_async_loop(rosobj, cond))
Expand Down
40 changes: 20 additions & 20 deletions src/rospy.jl
Expand Up @@ -10,17 +10,17 @@ Initialize this node, registering it with the ROS master. All arguments are pass
the rospy init_node function.
"""
init_node(name::AbstractString; args...) =
__rospy__[:init_node](ascii(name); args...)
__rospy__.init_node(ascii(name); args...)

"""
is_shutdown()
Return the shutdown status of the node.
"""
is_shutdown() = __rospy__[:is_shutdown]()
is_shutdown() = __rospy__.is_shutdown()

get_published_topics() = __rospy__[:get_published_topics]()
get_ros_root() = __rospy__[:get_ros_root]()
get_published_topics() = __rospy__.get_published_topics()
get_ros_root() = __rospy__.get_ros_root()

"""
spin()
Expand All @@ -45,9 +45,9 @@ default is given, throws a `KeyError` if the parameter cannot be found.
function get_param(param_name::AbstractString, def=nothing)
try
if def == nothing
__rospy__[:get_param](ascii(param_name))
__rospy__.get_param(ascii(param_name))
else
__rospy__[:get_param](ascii(param_name), def)
__rospy__.get_param(ascii(param_name), def)
end
catch ex
throw(KeyError(pycall(pybuiltin("str"), PyAny, ex.val)[2:end-1]))
Expand All @@ -60,15 +60,15 @@ end
Set the value of a parameter on the parameter server.
"""
set_param(param_name::AbstractString, val) =
__rospy__[:set_param](ascii(param_name), val)
__rospy__.set_param(ascii(param_name), val)

"""
has_param(param_name)
Return a boolean specifying if a parameter exists on the parameter server.
"""
has_param(param_name::AbstractString) =
__rospy__[:has_param](ascii(param_name))
__rospy__.has_param(ascii(param_name))

"""
delete_param(param_name)
Expand All @@ -77,23 +77,23 @@ Delete a parameter from the parameter server. Throws a `KeyError` if no such par
"""
function delete_param(param_name::AbstractString)
try
__rospy__[:delete_param](ascii(param_name))
__rospy__.delete_param(ascii(param_name))
catch ex
throw(KeyError(pycall(pybuiltin("str"), PyAny, ex.val)[2:end-1]))
end
end

#Doesn't work for some reason
#rospy_search_param(param_name::AbstractString) =
# __rospy__[:rospy_search_param](ascii(param_name))
get_param_names() = __rospy__[:get_param_names]()
# __rospy__.rospy_search_param(ascii(param_name))
get_param_names() = __rospy__.get_param_names()

#Logging API
logdebug(msg, args...) = __rospy__[:logdebug](msg, args...)
loginfo(msg, args...) = __rospy__[:loginfo](msg, args...)
logwarn(msg, args...) = __rospy__[:logwarn](msg, args...)
logerr(msg, args...) = __rospy__[:logerr](msg, args...)
logfatal(msg, args...) = __rospy__[:logfatal](msg, args...)
logdebug(msg, args...) = __rospy__.logdebug(msg, args...)
loginfo(msg, args...) = __rospy__.loginfo(msg, args...)
logwarn(msg, args...) = __rospy__.logwarn(msg, args...)
logerr(msg, args...) = __rospy__.logerr(msg, args...)
logfatal(msg, args...) = __rospy__.logfatal(msg, args...)

"""
logdebug, loginfo, logwarn, logerr, logfatal
Expand All @@ -104,7 +104,7 @@ arguments directly.
logdebug, loginfo, logwarn, logerr, logfatal

#Node information
get_name() = __rospy__[:get_name]()
get_namespace() = __rospy__[:get_namespace]()
get_node_uri() = __rospy__[:get_node_uri]()
get_caller_id() = __rospy__[:get_caller_id]()
get_name() = __rospy__.get_name()
get_namespace() = __rospy__.get_namespace()
get_node_uri() = __rospy__.get_node_uri()
get_caller_id() = __rospy__.get_caller_id()

0 comments on commit 7fd1aee

Please sign in to comment.