first commit
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
.vscode
|
||||||
|
examples/authfiles
|
||||||
29
Manifest.toml
Normal file
29
Manifest.toml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# This file is machine-generated - editing it directly is not advised
|
||||||
|
|
||||||
|
[[Base64]]
|
||||||
|
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
|
||||||
|
|
||||||
|
[[InteractiveUtils]]
|
||||||
|
deps = ["Markdown"]
|
||||||
|
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
|
||||||
|
|
||||||
|
[[Libdl]]
|
||||||
|
uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
|
||||||
|
|
||||||
|
[[Logging]]
|
||||||
|
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"
|
||||||
|
|
||||||
|
[[Markdown]]
|
||||||
|
deps = ["Base64"]
|
||||||
|
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
|
||||||
|
|
||||||
|
[[Random]]
|
||||||
|
deps = ["Serialization"]
|
||||||
|
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||||
|
|
||||||
|
[[Serialization]]
|
||||||
|
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
|
||||||
|
|
||||||
|
[[Test]]
|
||||||
|
deps = ["InteractiveUtils", "Logging", "Random", "Serialization"]
|
||||||
|
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
||||||
9
Project.toml
Normal file
9
Project.toml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
name = "Mosquitto"
|
||||||
|
uuid = "db317de6-444b-4dfa-9d0e-fbf3d8dd78ea"
|
||||||
|
authors = ["Christian Dengler <sumo_spider@yahoo.de>"]
|
||||||
|
version = "0.4.1"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
|
||||||
|
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||||
|
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
||||||
164
README.md
Normal file
164
README.md
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
# Mosquitto.jl
|
||||||
|
|
||||||
|
A wrapper around the Mosquitto C Api. The package provides easy to use MQTT client functionality.
|
||||||
|
|
||||||
|
## Package Status
|
||||||
|
* **Linux + Julia v1.6.x** has trouble when using multiple threads. You need to upgrade to 1.7 or use single thread with manual "loop" calls for that specific configuration.
|
||||||
|
MQTT v5 features like properties are not yet implemented. If you have the need for those, feel free to add an request on Github.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
* Install the mosquitto library
|
||||||
|
Follow the instructions at https://mosquitto.org/download/
|
||||||
|
* Download the julia package
|
||||||
|
`]add https://github.com/denglerchr/Mosquitto.jl`
|
||||||
|
|
||||||
|
## Basic Usage
|
||||||
|
|
||||||
|
### Connect to a broker
|
||||||
|
|
||||||
|
```julia
|
||||||
|
using Mosquitto
|
||||||
|
client = Client("test.mosquitto.org", 1883)
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a client using the ip and port of the broker. If you use >1 julia thread, the network loop will start immediately.
|
||||||
|
Use ?Mosquitto.Client for information on client settings.
|
||||||
|
|
||||||
|
### Publish a message
|
||||||
|
```julia
|
||||||
|
topic = "test"
|
||||||
|
message = "hello world"
|
||||||
|
publish(client, topic, message)
|
||||||
|
|
||||||
|
# only necessary if network loop isnt running in seprate thread
|
||||||
|
!client.status.loop_status && loop(client)
|
||||||
|
```
|
||||||
|
|
||||||
|
A message can be of type string, or of a type that can be converted to a Vector{UInt8} using reinterpret. If you do not use multiple threads and *loop_start(client)*, publishing might not happen until you call *loop(client)*.
|
||||||
|
|
||||||
|
### Subscribe to a topic
|
||||||
|
```julia
|
||||||
|
topic = "test"
|
||||||
|
subscribe(client, topic)
|
||||||
|
```
|
||||||
|
The subscription will vanish on disonnect. To automatically reconnect, you should subscribe after a connection was detected. Please look at the example *examples/03_subscribe_conconnect.jl*
|
||||||
|
|
||||||
|
### Simple example
|
||||||
|
|
||||||
|
This example scripts will
|
||||||
|
1) create a connection to a public broker
|
||||||
|
2) subscribes to the topic "jltest"
|
||||||
|
3) publish 2 messages to the same topic "jltest"
|
||||||
|
4) read and print the messages.
|
||||||
|
Note that the script might print 3 messages if a message for that topic is "retained".
|
||||||
|
|
||||||
|
```julia
|
||||||
|
using Mosquitto
|
||||||
|
|
||||||
|
# 1)
|
||||||
|
client = Client("test.mosquitto.org", 1883)
|
||||||
|
|
||||||
|
# 2)
|
||||||
|
topic = "jltest"
|
||||||
|
subscribe(client, topic)
|
||||||
|
|
||||||
|
# 3)
|
||||||
|
# Send 2 messages, first one will remain in the broker an be received on new connect
|
||||||
|
publish(client, topic, "Hi from Julia"; retain = true)
|
||||||
|
publish(client, topic, "Another message"; retain = false)
|
||||||
|
|
||||||
|
# lets wait to be sure to receive something
|
||||||
|
# or call the loop during that time, to make sure stuff is sent/received
|
||||||
|
client.status.loop_status ? sleep(3) : loop(client; timeout = 500, ntimes = 10)
|
||||||
|
|
||||||
|
# 4)
|
||||||
|
nmessages = Base.n_avail(Mosquitto.messages_channel)
|
||||||
|
for i = 1:nmessages
|
||||||
|
msg = take!(Mosquitto.messages_channel) # Tuple{String, Vector{UInt8})
|
||||||
|
println("Topic: $(msg.topic)\tMessage: $(String(msg.payload))")
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Usage and Notes
|
||||||
|
|
||||||
|
### Callbacks on messages or connection/disconnection
|
||||||
|
While the mosquitto C library requires callback functions, this package uses Channels to indicate the receiving of a message or the connection/disconnection to/from a broker. You should `take!(channel)` on these, possibly after checking for the number of available messages if not run in a separate thread. The two channels can be accessed via:
|
||||||
|
* `get_messages_channel()` or `Mosquitto.messages_channel`
|
||||||
|
* `get_connect_channel()` or `Mosquitto.connect_channel`
|
||||||
|
|
||||||
|
### Use a single threads or multiple threads
|
||||||
|
For simplicity of use, the network loop is executed in parallel when using multiple threads. This can in some cases lead to problems, e.g., when using multiple clients, as running multiple loops in parallel is not supported currently. Therefore, client loops should be run in sequence, see *examples/multiple_clients.jl* for an example.
|
||||||
|
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
You find examples in the example folder for how to use TLS connections and user/password authetication. Currently bad credentials do not lead to any error or warning, your messages will just not be sent and you will not receive any messages.
|
||||||
|
|
||||||
|
### Advanced example
|
||||||
|
|
||||||
|
```julia
|
||||||
|
# Read 20 messages in topic "test/..." from the public broker test.mosquitto.org
|
||||||
|
# Different from the previous example, the client will resubscribe to its topic every time it connects to the broker
|
||||||
|
using Mosquitto
|
||||||
|
|
||||||
|
# Connect to a broker using tls and username/password authetication.
|
||||||
|
# The CA certificate can be downloaded from the mosquitto page https://test.mosquitto.org/ssl/mosquitto.org.crt
|
||||||
|
# The connect function will not start a network loop in parallel, loop is triggered manually later.
|
||||||
|
client = Client()
|
||||||
|
const cafilepath = ... # add path to ca certificate here
|
||||||
|
tls_set(client, cafilepath)
|
||||||
|
connect(client, "test.mosquitto.org", 8885; username = "rw", password = "readwrite")
|
||||||
|
|
||||||
|
# Subscribe to topic "test" every time the client connects
|
||||||
|
# To know if there was a connection/disconnection, the channel Mosquitto.connect_channel
|
||||||
|
# or get_connect_channel() is used.
|
||||||
|
function onconnect(c)
|
||||||
|
# Check if something happened, else return 0
|
||||||
|
nmessages = Base.n_avail(get_connect_channel())
|
||||||
|
nmessages == 0 && return 0
|
||||||
|
|
||||||
|
# At this point, a connection or disconnection happened
|
||||||
|
for i = 1:nmessages
|
||||||
|
conncb = take!(get_connect_channel())
|
||||||
|
if conncb.val == 1
|
||||||
|
println("Connection of client $(conncb.clientptr) successfull, subscribing to test/#")
|
||||||
|
subscribe(c, "test/#")
|
||||||
|
elseif conncb.val == 0
|
||||||
|
println("Client $(conncb.clientptr) disconnected")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nmessages
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Print a message if it is received.
|
||||||
|
# To know if a message was received, we use the Mosquitto.messages_channel
|
||||||
|
# or get_messages_channel().
|
||||||
|
function onmessage(mrcount)
|
||||||
|
# Check if something happened, else return 0
|
||||||
|
nmessages = Base.n_avail(get_messages_channel())
|
||||||
|
nmessages == 0 && return 0
|
||||||
|
|
||||||
|
# At this point, a message was received, lets process it
|
||||||
|
for i = 1:nmessages
|
||||||
|
temp = take!(get_messages_channel())
|
||||||
|
println("Message $(mrcount+i):")
|
||||||
|
message = String(temp.payload)
|
||||||
|
length(message) > 20 && (message = message[1:18]*"...")
|
||||||
|
println("\ttopic: $(temp.topic)\tmessage:$(message)")
|
||||||
|
end
|
||||||
|
return nmessages
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# We trigger the loop manually until we have received at least
|
||||||
|
# 20 messages
|
||||||
|
mrcount = 0
|
||||||
|
while mrcount < 20
|
||||||
|
loop(client) # network loop
|
||||||
|
onconnect(client) # check for connection/disconnection
|
||||||
|
mrcount += onmessage(mrcount) # check for messages
|
||||||
|
end
|
||||||
|
|
||||||
|
# Disconnect the client everything
|
||||||
|
disconnect(client)
|
||||||
|
```
|
||||||
11
examples/01_publish.jl
Normal file
11
examples/01_publish.jl
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Mosquitto, Dates
|
||||||
|
|
||||||
|
# connect to a broker, also start loop if Threads.nthreads() > 1
|
||||||
|
client = Client("test.mosquitto.org")
|
||||||
|
|
||||||
|
topic = "test/julia"
|
||||||
|
message = "Hello World from Julia, send on $(now(UTC)) using the Mosquitto wrapper https://github.com/denglerchr/Mosquitto.jl"
|
||||||
|
publish(client, topic, message; retain = true)
|
||||||
|
!client.status.loop_status && loop(client; ntimes = 2)
|
||||||
|
|
||||||
|
disconnect(client)
|
||||||
32
examples/02_subscribe_simple.jl
Normal file
32
examples/02_subscribe_simple.jl
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Read 20 messages in topic "test/..." from the public broker test.mosquitto.org
|
||||||
|
using Mosquitto
|
||||||
|
|
||||||
|
# Connect to a broker, also starts loop if Threads.nthreads()>1
|
||||||
|
client = Client("test.mosquitto.org", 1883)
|
||||||
|
|
||||||
|
# subscribe to topic "test"
|
||||||
|
subscribe(client, "test/#")
|
||||||
|
|
||||||
|
function onmessage(nmin)
|
||||||
|
nmessages = Base.n_avail(Mosquitto.messages_channel)
|
||||||
|
nmessages == 0 && return 0
|
||||||
|
|
||||||
|
for i = 1:nmessages
|
||||||
|
temp = take!(Mosquitto.messages_channel)
|
||||||
|
println("Message $(nmin + i) of 20:")
|
||||||
|
println("\ttopic: $(temp.topic)\tmessage:$(String(temp.payload))")
|
||||||
|
end
|
||||||
|
return nmessages
|
||||||
|
end
|
||||||
|
|
||||||
|
# Messages will be put in
|
||||||
|
# the channel Mosquitto.messages_channel.
|
||||||
|
nmessages = 0
|
||||||
|
while nmessages<20
|
||||||
|
# Take the message on arrival
|
||||||
|
loop(client)
|
||||||
|
nmessages += onmessage(nmessages)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Close everything
|
||||||
|
disconnect(client)
|
||||||
29
examples/02_subscribe_simple_threaded.jl
Normal file
29
examples/02_subscribe_simple_threaded.jl
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Read 20 messages in topic "test/..." from the public broker test.mosquitto.org
|
||||||
|
# This example assumes julia was started with >1 thread
|
||||||
|
# e.g., julia -t 2 subscribe.jl
|
||||||
|
if Threads.nthreads()<2
|
||||||
|
println("Start julia using atleast 2 threads to run this example:")
|
||||||
|
println("julia -t 2 subscribe.jl")
|
||||||
|
exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
using Mosquitto
|
||||||
|
|
||||||
|
# Connect to a broker, also starts loop if Threads.nthreads()>1
|
||||||
|
client = Client("test.mosquitto.org", 1883)
|
||||||
|
|
||||||
|
# subscribe to topic "test"
|
||||||
|
subscribe(client, "test/#")
|
||||||
|
|
||||||
|
# Messages will be put in
|
||||||
|
# the channel Mosquitto.messages_channel.
|
||||||
|
for i = 1:20
|
||||||
|
# Take the message on arrival
|
||||||
|
temp = take!(Mosquitto.messages_channel)
|
||||||
|
# Do something with the message
|
||||||
|
println("Message $i of 20:")
|
||||||
|
println("\ttopic: $(temp.topic)\tmessage:$(String(temp.payload))")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Close everything
|
||||||
|
disconnect(client)
|
||||||
56
examples/03_subscribe_onconnect.jl
Normal file
56
examples/03_subscribe_onconnect.jl
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Read 20 messages in topic "test/..." from the public broker test.mosquitto.org
|
||||||
|
# Different from example 02, the client will resubscribe to its topic every time it connects to the broker
|
||||||
|
using Mosquitto
|
||||||
|
|
||||||
|
# Connect to a broker, but dont start network loop.
|
||||||
|
# We will trigger the network loop manually here using the loop function
|
||||||
|
client = Client("test.mosquitto.org", 1883; startloop = false)
|
||||||
|
|
||||||
|
# subscribe to topic "test" every time the client connects
|
||||||
|
function onconnect(c)
|
||||||
|
# Check if something happened, else return 0
|
||||||
|
nmessages = Base.n_avail(get_connect_channel())
|
||||||
|
nmessages == 0 && return 0
|
||||||
|
|
||||||
|
# At this point, a connection or disconnection happened
|
||||||
|
for i = 1:nmessages
|
||||||
|
conncb = take!(get_connect_channel())
|
||||||
|
if conncb.val == 1
|
||||||
|
println("Connection of client $(conncb.clientptr) successfull, subscribing to test/#")
|
||||||
|
subscribe(c, "test/#")
|
||||||
|
elseif conncb.val == 0
|
||||||
|
println("Client $(conncb.clientptr) disconnected")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nmessages
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function onmessage(mrcount)
|
||||||
|
# Check if something happened, else return 0
|
||||||
|
nmessages = Base.n_avail(get_messages_channel())
|
||||||
|
nmessages == 0 && return 0
|
||||||
|
|
||||||
|
# At this point, a message was received, lets process it
|
||||||
|
for i = 1:nmessages
|
||||||
|
temp = take!(get_messages_channel())
|
||||||
|
println("Message $(mrcount+i):")
|
||||||
|
message = String(temp.payload)
|
||||||
|
length(message) > 20 && (message = message[1:18]*"...")
|
||||||
|
println("\ttopic: $(temp.topic)\tmessage:$(message)")
|
||||||
|
end
|
||||||
|
return nmessages
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Messages will be put as a tuple in
|
||||||
|
# the channel Mosquitto.messages_channel.
|
||||||
|
mrcount = 0
|
||||||
|
while mrcount < 20
|
||||||
|
loop(client) # network loop
|
||||||
|
onconnect(client) # check for connection/disconnection
|
||||||
|
mrcount += onmessage(mrcount) # check for messages
|
||||||
|
end
|
||||||
|
|
||||||
|
# Close everything
|
||||||
|
disconnect(client)
|
||||||
47
examples/03_subscribe_onconnect_threaded.jl
Normal file
47
examples/03_subscribe_onconnect_threaded.jl
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# Read 20 messages in topic "test/..." from the public broker test.mosquitto.org
|
||||||
|
# Different from example 02, the client will resubscribe to its topic every time it connects to the broker
|
||||||
|
# This example assumes julia was started with >1 thread
|
||||||
|
# e.g., julia -t 2 subscribe.jl
|
||||||
|
if Threads.nthreads()<2
|
||||||
|
println("Start julia using atleast 2 threads to run this example:")
|
||||||
|
println("julia -t 2 subscribe.jl")
|
||||||
|
exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
using Mosquitto
|
||||||
|
|
||||||
|
# Connect to a broker, also starts loop if Threads.nthreads()>1
|
||||||
|
client = Client("test.mosquitto.org", 1883)
|
||||||
|
|
||||||
|
# subscribe to topic "test" every time the client connects
|
||||||
|
function subonconnect(c)
|
||||||
|
while true
|
||||||
|
conncb = take!(get_connect_channel())
|
||||||
|
if conncb.val == 1
|
||||||
|
println("Connection of client $(conncb.clientptr) successfull, subscribing to test/#")
|
||||||
|
subscribe(c, "test/#")
|
||||||
|
elseif conncb.val == 0
|
||||||
|
println("Client $(conncb.clientptr) disconnected")
|
||||||
|
else
|
||||||
|
println("Subonconnect function returning")
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
Threads.@spawn subonconnect(client)
|
||||||
|
|
||||||
|
# Messages will be put as a tuple in
|
||||||
|
# the channel Mosquitto.messages_channel.
|
||||||
|
for i = 1:50
|
||||||
|
# Take the message on arrival
|
||||||
|
temp = take!(get_messages_channel())
|
||||||
|
# Do something with the message
|
||||||
|
println("Message $i of 50:")
|
||||||
|
message = String(temp.payload)
|
||||||
|
length(message) > 15 && (message = message[1:13]*"...")
|
||||||
|
println("\ttopic: $(temp.topic)\tmessage:$(message)")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Close everything
|
||||||
|
put!(Mosquitto.connect_channel, Mosquitto.ConnectionCB(Ptr{Mosquitto.Cmosquitto}(C_NULL), UInt8(255), 0))
|
||||||
|
disconnect(client)
|
||||||
68
examples/04_multiple_clients.jl
Normal file
68
examples/04_multiple_clients.jl
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# Connect to 3 clients, 1 on the test server of mosquitto and 2 on localhost (requires a broker to run on localhost:1883)
|
||||||
|
# We run this only with manual loop execution, as multiple client with threaded network loop are currently not supported.
|
||||||
|
# What this script does:
|
||||||
|
# The client 1 will subscribe to messages, and every time a message is received in the correct topic test/julia
|
||||||
|
# client 2 will publish a message to localhost which is again received by client 3
|
||||||
|
|
||||||
|
using Mosquitto
|
||||||
|
|
||||||
|
# Connect to a broker, also starts loop if Threads.nthreads()>1
|
||||||
|
client1 = Client("test.mosquitto.org", 1883, startloop = false) # will receive message
|
||||||
|
client2 = Client("localhost", 1883, startloop = false) # will publish to localhost
|
||||||
|
client3 = Client("localhost", 1883, startloop = false) # will receive from localhost
|
||||||
|
|
||||||
|
# subscribe to topic different topics for each client
|
||||||
|
function subonconnect(c1::Client, c2::Client, c3::Client)
|
||||||
|
# check channel, if there is something todo
|
||||||
|
nmessages = Base.n_avail(get_connect_channel())
|
||||||
|
nmessages == 0 && return 0
|
||||||
|
for i = 1:nmessages
|
||||||
|
conncb = take!(get_connect_channel())
|
||||||
|
if conncb.val == 1
|
||||||
|
println("$(conncb.clientptr): connection successfull")
|
||||||
|
conncb.clientptr == c1.cptr.mosc && subscribe(c1, "test/#")
|
||||||
|
conncb.clientptr == c3.cptr.mosc && subscribe(c3, "julia")
|
||||||
|
elseif conncb.val == 0
|
||||||
|
println("$(conncb.clientptr): disconnected")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
# What to do if there is a message
|
||||||
|
function onmessage(c1, c2)
|
||||||
|
rand()<0.1 && publish(c1, "test/julia", "From client 1"; retain = false)
|
||||||
|
|
||||||
|
nmessages = Base.n_avail(get_messages_channel())
|
||||||
|
nmessages == 0 && return 0
|
||||||
|
for i = 1:nmessages
|
||||||
|
temp = take!(get_messages_channel())
|
||||||
|
# Do something with the message
|
||||||
|
if temp.topic == "test/julia"
|
||||||
|
println("\ttopic: $(temp.topic)\tmessage:$(String(temp.payload))")
|
||||||
|
publish(c2, "julia", "From client 2"; qos = 2)
|
||||||
|
elseif temp.topic == "julia"
|
||||||
|
println("\ttopic: $(temp.topic)\tmessage:$(String(temp.payload))")
|
||||||
|
else
|
||||||
|
println("Wrong topic :(")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
# Messages will be put as a Message struct
|
||||||
|
# the channel Mosquitto.messages_channel.
|
||||||
|
for i = 1:200
|
||||||
|
loop(client1; timeout = 100)
|
||||||
|
loop(client2; timeout = 100)
|
||||||
|
loop(client3; timeout = 100)
|
||||||
|
|
||||||
|
subonconnect(client1, client2, client3)
|
||||||
|
onmessage(client1, client2)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Close everything
|
||||||
|
disconnect(client1)
|
||||||
|
disconnect(client2)
|
||||||
|
disconnect(client3)
|
||||||
26
examples/05_tls_cert.jl
Normal file
26
examples/05_tls_cert.jl
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using Mosquitto
|
||||||
|
|
||||||
|
# defined paths to cafile, certfile, keyfile
|
||||||
|
include("authfiles/certpaths.jl")
|
||||||
|
|
||||||
|
# Create client, but dont connect yet
|
||||||
|
client = Client()
|
||||||
|
|
||||||
|
# Configure tls by providing crt and key files, needs to be done before connecting
|
||||||
|
tls_set(client, cafile; certfile = certfile, keyfile = keyfile)
|
||||||
|
|
||||||
|
# Connect
|
||||||
|
connect(client, "test.mosquitto.org", 8884)
|
||||||
|
|
||||||
|
# Rest as usual, subscribe and publish and read messages
|
||||||
|
subscribe(client, "test")
|
||||||
|
publish(client, "test/julia", "hello"; retain = false)
|
||||||
|
client.status.loop_status ? sleep(1) : loop(client; ntimes = 10)
|
||||||
|
|
||||||
|
nmessages = Base.n_avail(Mosquitto.messages_channel)
|
||||||
|
for i = 1:nmessages
|
||||||
|
msg = take!(Mosquitto.messages_channel) # Tuple{String, Vector{UInt8})
|
||||||
|
println("Topic: $(msg.topic)\tMessage: $(String(msg.payload))")
|
||||||
|
end
|
||||||
|
|
||||||
|
disconnect(client)
|
||||||
26
examples/06_tls_pw.jl
Normal file
26
examples/06_tls_pw.jl
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using Mosquitto
|
||||||
|
|
||||||
|
# import paths to the server ca file (can be downloaded from https://test.mosquitto.org/ )
|
||||||
|
include("authfiles/certpaths.jl")
|
||||||
|
|
||||||
|
# Create client, but dont connect yet
|
||||||
|
client = Client()
|
||||||
|
|
||||||
|
# Configure tls using the ca certificate, needs to be done before connecting
|
||||||
|
tls_set(client, cafile)
|
||||||
|
|
||||||
|
# Connect using username and password
|
||||||
|
connect(client, "test.mosquitto.org", 8885; username = "rw", password = "readwrite")
|
||||||
|
|
||||||
|
# Rest as usual, subscribe and publish and read messages
|
||||||
|
subscribe(client, "test")
|
||||||
|
publish(client, "test/julia", "hello"; retain = false)
|
||||||
|
client.status.loop_status ? sleep(1) : loop(client; ntimes = 10)
|
||||||
|
|
||||||
|
nmessages = Base.n_avail(Mosquitto.messages_channel)
|
||||||
|
for i = 1:nmessages
|
||||||
|
msg = take!(Mosquitto.messages_channel) # Tuple{String, Vector{UInt8})
|
||||||
|
println("Topic: $(msg.topic)\tMessage: $(String(msg.payload))")
|
||||||
|
end
|
||||||
|
|
||||||
|
disconnect(client)
|
||||||
39
src/Mosquitto.jl
Normal file
39
src/Mosquitto.jl
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Documentation
|
||||||
|
# https://mosquitto.org/api/files/mosquitto-h.html#mosquitto_message_callback_set
|
||||||
|
# https://github.com/eclipse/mosquitto/blob/master/include/mosquitto.h
|
||||||
|
module Mosquitto
|
||||||
|
|
||||||
|
import Base.finalizer
|
||||||
|
using Random, Libdl
|
||||||
|
|
||||||
|
# find library
|
||||||
|
const libmosquitto = @static if Sys.isunix()
|
||||||
|
Libdl.find_library(["libmosquitto", "libmosquitto.so.1"])
|
||||||
|
elseif Sys.iswindows()
|
||||||
|
Libdl.find_library("mosquitto.dll", [raw"C:\Program Files\Mosquitto"])
|
||||||
|
end
|
||||||
|
|
||||||
|
function __init__()
|
||||||
|
libmosquitto == "" && throw("Could not find the mosquitto library. If you're sure that it's installed, try adding it to DL_LOAD_PATH and rebuild the package.")
|
||||||
|
mosq_error_code = ccall((:mosquitto_lib_init, libmosquitto), Cint, ())
|
||||||
|
mosq_error_code != 0 && println("Mosquitto init returned error code $mosq_error_code")
|
||||||
|
v = lib_version()
|
||||||
|
v[1] != 2 || v[2] != 0 && println("Found lib version $(v[1]).$(v[2]), which is different from 2.0. Some functionality might not work")
|
||||||
|
atexit(lib_cleanup)
|
||||||
|
end
|
||||||
|
|
||||||
|
include("helpers.jl")
|
||||||
|
|
||||||
|
include("cwrapper.jl")
|
||||||
|
export lib_version
|
||||||
|
|
||||||
|
include("callbacks.jl")
|
||||||
|
export get_messages_channel, get_connect_channel
|
||||||
|
|
||||||
|
include("client.jl")
|
||||||
|
export Client, connect, reconnect, disconnect, publish, subscribe, unsubscribe, loop, tls_set, tls_psk_set
|
||||||
|
|
||||||
|
include("looprunner.jl")
|
||||||
|
export loop_start, loop_stop
|
||||||
|
|
||||||
|
end # module
|
||||||
84
src/callbacks.jl
Normal file
84
src/callbacks.jl
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
"""
|
||||||
|
struct MessageCB with fields
|
||||||
|
* topic:: String
|
||||||
|
* payload::Vector{UInt8}
|
||||||
|
|
||||||
|
A struct containing incoming message information and payload.
|
||||||
|
"""
|
||||||
|
struct MessageCB
|
||||||
|
topic::String
|
||||||
|
payload::Vector{UInt8}
|
||||||
|
end
|
||||||
|
|
||||||
|
"""
|
||||||
|
struct ConnectionCB with fields
|
||||||
|
* clientptr::Ptr
|
||||||
|
* val::UInt8
|
||||||
|
* returncode::Cint
|
||||||
|
|
||||||
|
The clientptr contains the ptr of the client that connected or disconnected.
|
||||||
|
This allows to distinguish between clients.
|
||||||
|
val is 0 on disconnect and 1 on connect.
|
||||||
|
returncode is the MQTT return code which can be used to identify, e.g., the reason for a disconnect.
|
||||||
|
"""
|
||||||
|
struct ConnectionCB
|
||||||
|
clientptr::Ptr{Cmosquitto}
|
||||||
|
val::UInt8
|
||||||
|
returncode::Cint
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
const messages_channel = Channel{MessageCB}(20)
|
||||||
|
const connect_channel = Channel{ConnectionCB}(5)
|
||||||
|
|
||||||
|
"""
|
||||||
|
get_messages_channel()
|
||||||
|
|
||||||
|
Returns the channel to which received messages are sent. The channel is a Channel{MessageCB}(20).
|
||||||
|
See ?Mosquitto.MessageCB for information on the struct
|
||||||
|
"""
|
||||||
|
get_messages_channel() = messages_channel
|
||||||
|
|
||||||
|
"""
|
||||||
|
get_connect_channel()
|
||||||
|
|
||||||
|
Returns the channel to which event notifications for connections or disconnections are sent. The channel is a Channel{ConnectionCB}(5).
|
||||||
|
See ?Mosquitto.ConnectionCB for information on the struct
|
||||||
|
"""
|
||||||
|
get_connect_channel() = connect_channel
|
||||||
|
|
||||||
|
|
||||||
|
# This callback function puts any message on arrival in the channel
|
||||||
|
# messages_channel which is a Channel{Mosquitto.Message}(20)
|
||||||
|
function callback_message(mos::Ptr{Cmosquitto}, obj::Ptr{Cvoid}, message::Ptr{CMosquittoMessage}) #, clientid::String)
|
||||||
|
# get topic and payload from the message
|
||||||
|
jlmessage = unsafe_load(message)
|
||||||
|
jlpayload = [unsafe_load(jlmessage.payload, i) for i = 1:jlmessage.payloadlen]
|
||||||
|
topic = unsafe_string(jlmessage.topic)
|
||||||
|
|
||||||
|
# put it in the channel for further use
|
||||||
|
if Base.n_avail(messages_channel)>=messages_channel.sz_max
|
||||||
|
take!(messages_channel)
|
||||||
|
end
|
||||||
|
put!(messages_channel, MessageCB(topic, jlpayload))
|
||||||
|
|
||||||
|
return nothing
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function callback_connect(mos::Ptr{Cmosquitto}, obj::Ptr{Cvoid}, rc::Cint)
|
||||||
|
if Base.n_avail(connect_channel)>=connect_channel.sz_max
|
||||||
|
take!(connect_channel)
|
||||||
|
end
|
||||||
|
put!( connect_channel, ConnectionCB(mos, one(UInt8), rc ) )
|
||||||
|
return nothing
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function callback_disconnect(mos::Ptr{Cmosquitto}, obj::Ptr{Cvoid}, rc::Cint)
|
||||||
|
if Base.n_avail(connect_channel)>=connect_channel.sz_max
|
||||||
|
take!(connect_channel)
|
||||||
|
end
|
||||||
|
put!( connect_channel, ConnectionCB(mos, zero(UInt8), rc ) )
|
||||||
|
return nothing
|
||||||
|
end
|
||||||
173
src/client.jl
Normal file
173
src/client.jl
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
import Base.n_avail, Base.show
|
||||||
|
|
||||||
|
|
||||||
|
struct Cobjs
|
||||||
|
mosc::Ref{Cmosquitto}
|
||||||
|
obj::Ref{Cvoid}
|
||||||
|
conncb::Ref{Cvoid}
|
||||||
|
dconncb::Ref{Cvoid}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
mutable struct MoscStatus
|
||||||
|
conn_status::Bool
|
||||||
|
loop_status::Bool
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
struct Client
|
||||||
|
id::String
|
||||||
|
cptr::Cobjs
|
||||||
|
loop_channel::AbstractChannel{Int}
|
||||||
|
status::MoscStatus
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function show(io::IO, client::Client)
|
||||||
|
println("MQTTClient_$(client.id)")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function finalizer(client::Client)
|
||||||
|
disconnect(client)
|
||||||
|
destroy(client.cptr.mosc)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Client(ip::String, port::Int=1883; kwargs...)
|
||||||
|
|
||||||
|
Create a client connection to an MQTT broker. Possible key word arguments are:
|
||||||
|
* id::String = randstring(15) The id should be unique per connection.
|
||||||
|
* connectme::Bool = true Connect immediately if true. If false, you need to manually use *connect(client, ip, port)* and input arguments are not used.
|
||||||
|
* startloop::Bool = true If true, and Threads.nthreads()>1, the network loop will be executed regularly after connection.
|
||||||
|
|
||||||
|
Client( ; id::String = randstring(15))
|
||||||
|
|
||||||
|
Create a client structure without connecting to a broker or starting a network loop.
|
||||||
|
"""
|
||||||
|
function Client(ip::String, port::Int=1883; id::String = randstring(15), connectme::Bool = true, startloop::Bool = true)
|
||||||
|
# Create a Client object
|
||||||
|
client = Client( ; id = id )
|
||||||
|
|
||||||
|
# Possibly Connect to broker
|
||||||
|
if connectme
|
||||||
|
flag = connect(client, ip, port)
|
||||||
|
flag != 0 && println("Connection to the broker failed")
|
||||||
|
|
||||||
|
# Start loop if it can be started without blocking
|
||||||
|
if flag == 0 && startloop && Threads.nthreads()>1
|
||||||
|
loop_start(client)
|
||||||
|
elseif startloop
|
||||||
|
println("Single thread, loop will be blocking, start it manually using loop_start(::Client) or call loop(client) regularly.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return client
|
||||||
|
end
|
||||||
|
|
||||||
|
function Client(; id::String = randstring(15))
|
||||||
|
# Create mosquitto object
|
||||||
|
cobj = Ref{Cvoid}()
|
||||||
|
cmosc = mosquitto_new(id, true, cobj)
|
||||||
|
|
||||||
|
# Set callbacks
|
||||||
|
#f_message_cb(mos, obj, message) = callback_message(mos, obj, message, id)
|
||||||
|
cfunc_message = @cfunction(callback_message, Cvoid, (Ptr{Cmosquitto}, Ptr{Cvoid}, Ptr{CMosquittoMessage}))
|
||||||
|
message_callback_set(cmosc, cfunc_message)
|
||||||
|
|
||||||
|
cfunc_connect = @cfunction(callback_connect, Cvoid, (Ptr{Cmosquitto}, Ptr{Cvoid}, Cint))
|
||||||
|
connect_callback_set(cmosc, cfunc_connect)
|
||||||
|
|
||||||
|
cfunc_disconnect = @cfunction(callback_disconnect, Cvoid, (Ptr{Cmosquitto}, Ptr{Cvoid}, Cint))
|
||||||
|
disconnect_callback_set(cmosc, cfunc_disconnect)
|
||||||
|
|
||||||
|
# Create object
|
||||||
|
loop_channel = Channel{Int}(1)
|
||||||
|
return Client(id, Cobjs(cmosc, cobj, cfunc_connect, cfunc_disconnect), loop_channel, MoscStatus(false, false) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
connect(client::Client, ip::String, port::Int; kwargs...)
|
||||||
|
|
||||||
|
Connect the client to a broker. kwargs are:
|
||||||
|
* username::String = "" A username, should one be required
|
||||||
|
* password::String = "" A password belonging to the username
|
||||||
|
* keepalive::Int = 60 Maximal of time the client has to send PINGREQ or a message before disconnection
|
||||||
|
"""
|
||||||
|
function connect(client::Client, ip::String, port::Int; username::String = "", password::String = "", keepalive::Int = 60)
|
||||||
|
if username != ""
|
||||||
|
flag = username_pw_set(client.cptr.mosc, username, password)
|
||||||
|
flag != 0 && println("Couldnt set password and username, error $flag")
|
||||||
|
end
|
||||||
|
flag = connect(client.cptr.mosc, ip; port = port, keepalive = keepalive)
|
||||||
|
flag == 0 ? (client.status.conn_status = true) : println("Connection to broker failed")
|
||||||
|
return flag
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
disconnect(client::Client)
|
||||||
|
"""
|
||||||
|
function disconnect(client::Client)
|
||||||
|
client.status.loop_status && loop_stop(client)
|
||||||
|
flag = disconnect(client.cptr.mosc)
|
||||||
|
flag == 0 && (client.status.conn_status = false)
|
||||||
|
return flag
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
reconnect(client::Client)
|
||||||
|
"""
|
||||||
|
function reconnect(client::Client)
|
||||||
|
flag = reconnect(client.cptr.mosc)
|
||||||
|
flag == 0 && (client.status.conn_status = true)
|
||||||
|
return flag
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
publish(client::Client, topic::String, payload; qos::Int = 1, retain::Bool = true)
|
||||||
|
|
||||||
|
Publish a message to the broker.
|
||||||
|
"""
|
||||||
|
publish(client::Client, topic::String, payload; qos::Int = 1, retain::Bool = true) = publish(client.cptr.mosc, topic, payload; qos = qos, retain = retain)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
subscribe(client::Client, topic::String; qos::Int = 1)
|
||||||
|
|
||||||
|
Subscribe to a topic. Received messages will be accessible Mosquitto.messages_channel as a Tuple{String, Vector{Uint8}}.
|
||||||
|
"""
|
||||||
|
subscribe(client::Client, topic::String; qos::Int = 1) = subscribe(client.cptr.mosc, topic; qos = qos)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
unsubscribe(client::Client, topic::String)
|
||||||
|
|
||||||
|
Unsubscribe from a topic.
|
||||||
|
"""
|
||||||
|
unsubscribe(client::Client, topic::String) = unsubscribe(client.cptr.mosc, topic)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
tls_set(client::Client, cafile::String; certfile::String = "", keyfile::String = "")
|
||||||
|
"""
|
||||||
|
function tls_set(client::Client, cafile::String; certfile::String = "", keyfile::String = "")
|
||||||
|
xor( certfile == "", keyfile == "" ) && throw("You need to either provide both cert and key files, or none of both")
|
||||||
|
if certfile == ""
|
||||||
|
return tls_set(client.cptr.mosc, cafile, C_NULL, C_NULL, C_NULL, C_NULL)
|
||||||
|
else
|
||||||
|
return tls_set(client.cptr.mosc, cafile, C_NULL, certfile, keyfile, C_NULL)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
tls_plk_set(client::Client, psk::String, identity::String, ciphers::Union{Nothing, String})
|
||||||
|
"""
|
||||||
|
function tls_psk_set(client::Client, psk::String, identity::String, ciphers::Union{Nothing, String} = nothing)
|
||||||
|
return tls_psk_set(client.cptr.mosc, psk, identity, ciphers)
|
||||||
|
end
|
||||||
137
src/cwrapper.jl
Normal file
137
src/cwrapper.jl
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
struct Cmosquitto end
|
||||||
|
|
||||||
|
struct CMosquittoMessage
|
||||||
|
mid::Cint
|
||||||
|
topic::Cstring
|
||||||
|
payload::Ptr{UInt8} # we treat payload as raw bytes
|
||||||
|
payloadlen::Cint
|
||||||
|
qos::Cint
|
||||||
|
retain::Bool
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function mosquitto_new(id::String, clean_start::Bool, obj)
|
||||||
|
return ccall((:mosquitto_new, libmosquitto), Ptr{Cmosquitto}, (Cstring, Bool, Ptr{Cvoid}), id, clean_start, obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function destroy(client::Ref{Cmosquitto})
|
||||||
|
return ccall((:mosquitto_destroy, libmosquitto), Cvoid, (Ptr{Cmosquitto},), client)
|
||||||
|
end
|
||||||
|
finalizer(client::Ref{Cmosquitto}) = destroy(client)
|
||||||
|
|
||||||
|
|
||||||
|
function connect(client::Ref{Cmosquitto}, host::String; port::Int = 1883, keepalive::Int = 60)
|
||||||
|
return ccall((:mosquitto_connect, libmosquitto), Cint, (Ptr{Cmosquitto}, Cstring, Cint, Cint), client, host, port, keepalive)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function reconnect(client::Ref{Cmosquitto})
|
||||||
|
ccall((:mosquitto_reconnect, libmosquitto), Cint, (Ptr{Cmosquitto},), client)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function disconnect(client::Ref{Cmosquitto})
|
||||||
|
return ccall((:mosquitto_disconnect, libmosquitto), Cint, (Ptr{Cmosquitto},), client)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function publish(client::Ref{Cmosquitto}, topic::String, payload; qos::Int = 1, retain::Bool = true)
|
||||||
|
payloadnew = getbytes(payload)
|
||||||
|
payloadlen = length(payloadnew) # dont use sizeof, as payloadnew might be of type "reinterpreted"
|
||||||
|
mid = Int[0]
|
||||||
|
msg_nr = ccall((:mosquitto_publish, libmosquitto), Cint,
|
||||||
|
(Ptr{Cmosquitto}, Ptr{Cint}, Cstring, Cint, Ptr{UInt8}, Cint, Bool),
|
||||||
|
client, mid, topic, payloadlen, payloadnew, qos, retain)
|
||||||
|
return msg_nr
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function subscribe(client::Ref{Cmosquitto}, sub::String; qos::Int = 1)
|
||||||
|
mid = zeros(Cint, 1)
|
||||||
|
msg_nr = ccall((:mosquitto_subscribe, libmosquitto), Cint,
|
||||||
|
(Ptr{Cmosquitto}, Ptr{Cint}, Cstring, Cint),
|
||||||
|
client, mid, sub, qos)
|
||||||
|
return msg_nr
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function unsubscribe(client::Ref{Cmosquitto}, sub::String)
|
||||||
|
mid = zeros(Cint, 1)
|
||||||
|
msg_nr = ccall((:mosquitto_unsubscribe, libmosquitto), Cint,
|
||||||
|
(Ptr{Cmosquitto}, Ptr{Cint}, Cstring),
|
||||||
|
client, mid, sub)
|
||||||
|
return msg_nr
|
||||||
|
end
|
||||||
|
|
||||||
|
#= Broken?
|
||||||
|
function loop_start(client::Ref{Cmosquitto})
|
||||||
|
msg_nr = ccall((:mosquitto_loop_start, libmosquitto), Cint, (Ptr{Cmosquitto},), client)
|
||||||
|
return msg_nr
|
||||||
|
end
|
||||||
|
|
||||||
|
function loop_stop(client::Ref{Cmosquitto}; force::Bool = false)
|
||||||
|
msg_nr = ccall((:mosquitto_loop_stop, libmosquitto), Cint, (Ptr{Cmosquitto}, Bool), client, force)
|
||||||
|
return msg_nr
|
||||||
|
end
|
||||||
|
=#
|
||||||
|
|
||||||
|
|
||||||
|
function loop_forever(client; timeout::Int = 1000, max_packets::Int = 1)
|
||||||
|
return ccall((:mosquitto_loop_forever, libmosquitto), Cint, (Ptr{Cmosquitto}, Cint, Cint), client, timeout, max_packets)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function loop(client; timeout::Int = 1000, max_packets::Int = 1)
|
||||||
|
return ccall((:mosquitto_loop, libmosquitto), Cint, (Ptr{Cmosquitto}, Cint, Cint), client, timeout, max_packets)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function connect_callback_set(client::Ref{Cmosquitto}, cfunc)
|
||||||
|
return ccall((:mosquitto_connect_callback_set, libmosquitto), Cvoid, (Ptr{Cmosquitto}, Ptr{Cvoid}), client, cfunc)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function disconnect_callback_set(client::Ref{Cmosquitto}, cfunc)
|
||||||
|
return ccall((:mosquitto_disconnect_callback_set, libmosquitto), Cvoid, (Ptr{Cmosquitto}, Ptr{Cvoid}), client, cfunc)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function message_callback_set(client::Ref{Cmosquitto}, cfunc)
|
||||||
|
ccall((:mosquitto_message_callback_set, libmosquitto), Cvoid, (Ptr{Cmosquitto}, Ptr{Cvoid}), client, cfunc)
|
||||||
|
return nothing
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function username_pw_set(client::Ref{Cmosquitto}, username::String, password::String)
|
||||||
|
#password != "" && (password = Cstring(C_NULL))
|
||||||
|
return ccall((:mosquitto_username_pw_set, libmosquitto), Cint, (Ptr{Cmosquitto}, Cstring, Cstring), client, username, password)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function tls_set(client::Ref{Cmosquitto}, cafile, capath, certfile, keyfile, callback::Ptr{Cvoid})
|
||||||
|
return ccall((:mosquitto_tls_set, libmosquitto), Cint, (Ptr{Cmosquitto}, Cstring, Cstring, Cstring, Cstring, Ptr{Cvoid}), client, cafile, capath, certfile, keyfile, callback)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function tls_psk_set(client::Ref{Cmosquitto}, psk::String, identity::String, ciphers::Nothing)
|
||||||
|
return ccall((:mosquitto_tls_psk_set, libmosquitto), Cint, (Ptr{Cmosquitto}, Cstring, Cstring, Cstring), client, psk, identity, C_NULL)
|
||||||
|
end
|
||||||
|
|
||||||
|
function tls_psk_set(client::Ref{Cmosquitto}, psk::String, identity::String, ciphers::String)
|
||||||
|
return ccall((:mosquitto_tls_psk_set, libmosquitto), Cint, (Ptr{Cmosquitto}, Cstring, Cstring, Cstring), client, psk, identity, ciphers)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lib_version()
|
||||||
|
maj = zeros(Int, 1)
|
||||||
|
min = zeros(Int, 1)
|
||||||
|
rev = zeros(Int, 1)
|
||||||
|
ccall((:mosquitto_lib_version, libmosquitto), Cint, (Ptr{Cint}, Ptr{Cint}, Ptr{Cint}), maj, min, rev)
|
||||||
|
return maj[1], min[1], rev[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lib_cleanup()
|
||||||
|
ccall((:mosquitto_lib_cleanup, libmosquitto), Cvoid, ())
|
||||||
|
end
|
||||||
5
src/helpers.jl
Normal file
5
src/helpers.jl
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# The function getbytes transforms the payload into a Vector representation of UInt8
|
||||||
|
@inline getbytes(in::String) = transcode(UInt8, in)
|
||||||
|
@inline getbytes(in::AbstractVector{UInt8}) = in
|
||||||
|
@inline getbytes(in::Number) = reinterpret(UInt8, [in])
|
||||||
|
@inline getbytes(in) = reinterpret(UInt8, in)
|
||||||
75
src/looprunner.jl
Normal file
75
src/looprunner.jl
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
"""
|
||||||
|
loop(client::Client; timeout::Int = 1000, ntimes::Int = 1)
|
||||||
|
|
||||||
|
Perform a network loop. This will get messages of subscriptions and send published messages.
|
||||||
|
"""
|
||||||
|
function loop(client::Client; timeout::Int = 1000, ntimes::Int = 1, autoreconnect::Bool = true)
|
||||||
|
out = zero(Cint)
|
||||||
|
for _ = 1:ntimes
|
||||||
|
out = loop(client.cptr.mosc; timeout = timeout)
|
||||||
|
if autoreconnect && out == 4
|
||||||
|
flag = reconnect(client)
|
||||||
|
client.status.conn_status = ifelse( flag == 0, true, false )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
loop_start(client::Client; autoreconnect::Bool = true)
|
||||||
|
|
||||||
|
This function keeps calling the network loop until loop_stop is called.
|
||||||
|
If only one thread is used, this function will be blocking, else the calls
|
||||||
|
will be executed on a worker thread.
|
||||||
|
"""
|
||||||
|
function loop_start(client::Client; autoreconnect::Bool = true)
|
||||||
|
if client.status.loop_status == true
|
||||||
|
println("Loop is already running")
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if Threads.nthreads()>1
|
||||||
|
client.status.loop_status = true
|
||||||
|
Threads.@spawn loop_runner(client, autoreconnect)
|
||||||
|
else
|
||||||
|
client.status.loop_status = true
|
||||||
|
loop_forever(client.cptr.mosc)
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
loop_stop(client::Client)
|
||||||
|
|
||||||
|
Stop the network loop.
|
||||||
|
"""
|
||||||
|
function loop_stop(client::Client)
|
||||||
|
if client.status.loop_status
|
||||||
|
put!(client.loop_channel, 0)
|
||||||
|
return fetch(client.loop_channel)
|
||||||
|
else
|
||||||
|
println("Loop not running")
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function loop_runner(client::Client, autoreconnect::Bool)
|
||||||
|
while isempty(client.loop_channel)
|
||||||
|
msg = loop(client.cptr.mosc)
|
||||||
|
|
||||||
|
if autoreconnect && msg == Cint(4)
|
||||||
|
# case of a disconnect, try reconnecting every 2 seconds
|
||||||
|
println("Client disconnected, trying to reconnect...")
|
||||||
|
reconnect(client) != 0 && sleep(2)
|
||||||
|
elseif msg != Cint(0)
|
||||||
|
client.status.loop_status = false
|
||||||
|
println("Loop failed with error $msg")
|
||||||
|
return msg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
client.status.loop_status = false
|
||||||
|
return take!(client.loop_channel)
|
||||||
|
end
|
||||||
49
test/runtests.jl
Normal file
49
test/runtests.jl
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using Mosquitto, Test, Random
|
||||||
|
|
||||||
|
topic = "jltest"*randstring(5)
|
||||||
|
message = [1, 2, 3]
|
||||||
|
|
||||||
|
client = Client("test.mosquitto.org", 1883)
|
||||||
|
|
||||||
|
@testset "General" begin
|
||||||
|
Threads.nthreads()>1 && @test client.status.loop_status == 1
|
||||||
|
Threads.nthreads()==1 && @test client.status.loop_status == 0
|
||||||
|
@test loop_stop(client) == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
@testset "Unauthenticated" begin
|
||||||
|
@test subscribe(client, topic) == 0
|
||||||
|
@test loop(client) == 0
|
||||||
|
while !isempty(Mosquitto.messages_channel)
|
||||||
|
# empty channel
|
||||||
|
take!(Mosquitto.messages_channel)
|
||||||
|
end
|
||||||
|
@test publish(client, topic, message; retain = false) == 0
|
||||||
|
loop(client, ntimes = 10)
|
||||||
|
@test Base.n_avail(Mosquitto.messages_channel) == 1
|
||||||
|
if Base.n_avail(Mosquitto.messages_channel) >= 1
|
||||||
|
@test Array(reinterpret(Int, take!(Mosquitto.messages_channel).payload)) == message
|
||||||
|
end
|
||||||
|
@test disconnect(client) == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
client = Client("", 0; connectme = false)
|
||||||
|
|
||||||
|
@testset "Authenticated" begin
|
||||||
|
@test connect(client, "test.mosquitto.org", 1884; username = "rw", password = "readwrite") == 0
|
||||||
|
@test subscribe(client, topic) == 0
|
||||||
|
@test loop(client) == 0
|
||||||
|
while !isempty(Mosquitto.messages_channel)
|
||||||
|
# empty channel
|
||||||
|
take!(Mosquitto.messages_channel)
|
||||||
|
end
|
||||||
|
@test publish(client, topic, message; retain = false) == 0
|
||||||
|
loop(client, ntimes = 5)
|
||||||
|
loop(client; ntimes = 2, timeout = 5000)
|
||||||
|
@test Base.n_avail(Mosquitto.messages_channel) == 1
|
||||||
|
if Base.n_avail(Mosquitto.messages_channel) >= 1
|
||||||
|
@test Array(reinterpret(Int, take!(Mosquitto.messages_channel).payload)) == message
|
||||||
|
end
|
||||||
|
@test disconnect(client) == 0
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user