Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use LCMCore to provide the LCM interface #14

Merged
merged 5 commits into from
Jan 29, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ os:
- linux
- osx
julia:
- 0.4
- 0.5
- nightly
notifications:
Expand Down
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
[![Build Status](https://travis-ci.org/rdeits/PyLCM.jl.svg?branch=master)](https://travis-ci.org/rdeits/PyLCM.jl)
[![codecov.io](https://codecov.io/github/rdeits/PyLCM.jl/coverage.svg?branch=master)](https://codecov.io/github/rdeits/PyLCM.jl?branch=master)

PyLCM provides an interface to the [Lightweight Communications and Marshalling (LCM) library](https://lcm-proj.github.io/) in Julia. It wraps the LCM Python interface using [PyCall](https://github.com/stevengj/PyCall.jl), so it will be slower than calling the LCM C-API directly.
PyLCM provides an interface to the [Lightweight Communications and Marshalling (LCM) library](https://lcm-proj.github.io/) in Julia. Most of the functionality is provided by [LCMCore.jl](https://github.com/rdeits/LCMCore.jl), which interacts with LCM through its C API. PyLCM builds on LCMCore by allowing you to send and receive Python LCM types from Julia.

# Installation

If you have a systemwide installation of LCM, PyLCM will try to use it. If you don't, then running `Pkg.build("PyLCM")` will download and install a private copy of LCM and the python bindings for you.
If you have a systemwide installation of LCM, PyLCM will try to use it. If you don't, then running `Pkg.build("LCMCore")` will download and install a private copy of LCM and the python bindings for you.

# Usage

Expand Down Expand Up @@ -65,3 +65,13 @@ while true
handle(lc)
end
```

### Asynchronously handling messages

Creating an asynchronous handler just requires the `@async` macro:

```julia
@async while true
handle(lc)
end
```
6 changes: 2 additions & 4 deletions REQUIRE
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
julia 0.4
julia 0.5
PyCall 1.4.0
BinDeps 0.4.0
Compat 0.8.0
@osx Homebrew 0.3.0
LCMCore 0.0.1
34 changes: 0 additions & 34 deletions appveyor.yml

This file was deleted.

85 changes: 0 additions & 85 deletions deps/build.jl

This file was deleted.

66 changes: 21 additions & 45 deletions src/PyLCM.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,35 @@ __precompile__()

module PyLCM

using Base.Dates: Period, Millisecond
using PyCall
using Base.Dates: Period, Millisecond
using LCMCore
import LCMCore: encode, subscribe, decode

export LCM, publish, subscribe, handle, @pyimport

const pylcm = PyNULL()

immutable LCM
lcm_obj::PyObject

LCM() = new(pylcm[:LCM]())
end

function publish(lc::LCM, channel::AbstractString, msg)
pycall(lc.lcm_obj[:publish], PyAny, channel, pycall(msg[:encode], PyObject))
end

function subscribe(lc::LCM, channel::AbstractString, handler::Function)
lc.lcm_obj[:subscribe](channel, pyeval("lambda chan, data, handler=h: handler(chan, bytearray(data))", h=handler))
end

function subscribe(lc::LCM, channel::AbstractString, handler::Function, msg_type::PyObject)
lc.lcm_obj[:subscribe](channel, pyeval("lambda chan, data, handler=h, msg_type=t: handler(chan, msg_type.decode(data))", h=handler, t=msg_type))
end

"Wait for and dispatch the next incoming message"
function handle(lc::LCM)
pycall(lc.lcm_obj[:handle], PyObject)
true
end

"""
handle(lc, timeout)
Wait for and dispatch the next incoming message, with a timeout expressed
as any Base.Dates.Period type. For example:
handle(lc, Millisecond(10))
or
handle(lc, Second(1))
Returns true if a message was handled, false if the function timed out.
"""
function handle(lc::LCM, timeout::Period)
timeout_ms = convert(Int, convert(Millisecond, timeout))
convert(Bool, pycall(lc.lcm_obj[:handle_timeout], PyObject, timeout_ms))
# This is the only method required to enable publishing python
# LCM messages
encode(msg::PyObject) = pycall(msg[:encode], Vector{UInt8})

# We override the subscribe() method that takes in a user-supplied
# message type. That's because the method in LCMCore assumes that
# the Julia type of the message is enough to determine how to
# decode the message. That's not the case for PyLCM because all
# python LCM types are just PyObjects.
function subscribe(lcm::LCM, channel::String, handler, msgtype::PyObject)
function inner_handler(channel, data)
pymsg = pycall(msgtype[:decode], PyObject, data)
handler(channel, pymsg)
end
subscribe(lcm, channel, inner_handler)
end

function __init__()
depsjl = joinpath(dirname(@__FILE__), "..", "deps", "deps.jl")
isfile(depsjl) ? include(depsjl) : error("PyLCM not properly ",
"installed. Please run\nPkg.build(\"PyLCM\")")
sys = pyimport("sys")
unshift!(PyVector(sys["path"]), joinpath(LCMCore.lcm_prefix, "lib", "python" * string(sys[:version_info][1]) * "." * string(sys[:version_info][2]), "site-packages"))
copy!(pylcm, pyimport("lcm"))
end

Expand Down