Initial commit
This commit is contained in:
commit
0c7c7ce6d9
|
@ -0,0 +1,9 @@
|
||||||
|
.DS_Store
|
||||||
|
.idea
|
||||||
|
*.log
|
||||||
|
tmp/
|
||||||
|
|
||||||
|
.cpcache/
|
||||||
|
.nrepl-port
|
||||||
|
target/
|
||||||
|
result
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
:paths ["src"]
|
||||||
|
:deps {
|
||||||
|
org.clojure/clojure { :mvn/version "1.11.1" }
|
||||||
|
org.clojure/core.async { :mvn/version "1.6.673" }
|
||||||
|
org.clojure/data.json { :mvn/version "2.4.0" }
|
||||||
|
|
||||||
|
org.eclipse.paho/org.eclipse.paho.client.mqttv3 { :mvn/version "1.2.5" }
|
||||||
|
}
|
||||||
|
:aliases {
|
||||||
|
:test {
|
||||||
|
:extra-paths ["test"]
|
||||||
|
:extra-deps {
|
||||||
|
io.github.cognitect-labs/test-runner
|
||||||
|
{
|
||||||
|
:git/url "https://github.com/cognitect-labs/test-runner.git"
|
||||||
|
:sha "dfb30dd6605cb6c0efc275e1df1736f6e90d4d73"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:main-opts ["-m" "cognitect.test-runner"]
|
||||||
|
:exec-fn cognitect.test-runner.api/test
|
||||||
|
}
|
||||||
|
:build { :default-ns build }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
(ns milquetoast.client
|
||||||
|
(:require [clojure.core.async :as async :refer [go go-loop <! >!]]
|
||||||
|
[clojure.data.json :as json])
|
||||||
|
(:import [org.eclipse.paho.client.mqttv3 MqttClient MqttConnectOptions MqttMessage IMqttMessageListener]
|
||||||
|
org.eclipse.paho.client.mqttv3.persist.MemoryPersistence))
|
||||||
|
|
||||||
|
(defn- create-mqtt-client! [broker-uri username password]
|
||||||
|
(let [client-id (MqttClient/generateClientId)
|
||||||
|
opts (doto (MqttConnectOptions.)
|
||||||
|
(.setCleanSession true)
|
||||||
|
(.setAutomaticReconnect true)
|
||||||
|
(.setPassword (char-array password))
|
||||||
|
(.setUserName username))]
|
||||||
|
(doto (MqttClient. broker-uri client-id (MemoryPersistence.))
|
||||||
|
(.connect opts))))
|
||||||
|
|
||||||
|
(defn- create-message
|
||||||
|
[msg {:keys [qos retain]
|
||||||
|
:or {qos 1
|
||||||
|
retain false}}]
|
||||||
|
(doto (MqttMessage. (.getBytes msg))
|
||||||
|
(.setQos qos)
|
||||||
|
(.setRetained retain)))
|
||||||
|
|
||||||
|
(defn- parse-message [mqtt-msg]
|
||||||
|
{
|
||||||
|
:id (.getId mqtt-msg)
|
||||||
|
:qos (.getQos mqtt-msg)
|
||||||
|
:retained (.isRetained mqtt-msg)
|
||||||
|
:duplicate (.isDuplicate mqtt-msg)
|
||||||
|
:payload (.toString mqtt-msg)
|
||||||
|
})
|
||||||
|
|
||||||
|
(defprotocol IMilquetoastClient
|
||||||
|
(send-message! [_ topic msg opts])
|
||||||
|
(add-channel! [_ chan])
|
||||||
|
(stop! [_])
|
||||||
|
(subscribe-topic! [_ topic opts]))
|
||||||
|
|
||||||
|
(defrecord MilquetoastClient [client open-channels verbose]
|
||||||
|
IMilquetoastClient
|
||||||
|
(send-message! [_ topic msg opts]
|
||||||
|
(.publish client topic (create-message msg opts)))
|
||||||
|
(stop! [_]
|
||||||
|
(when verbose
|
||||||
|
(println
|
||||||
|
(str "stopping " (count @open-channels) " channels")))
|
||||||
|
(doseq [chan @open-channels]
|
||||||
|
(go (>! chan :stop)
|
||||||
|
(async/close! chan)))
|
||||||
|
true)
|
||||||
|
(add-channel! [_ chan]
|
||||||
|
(swap! open-channels conj chan))
|
||||||
|
(subscribe-topic! [self topic opts]
|
||||||
|
(let [{:keys [buffer-size]} opts
|
||||||
|
chan (async/chan buffer-size)]
|
||||||
|
(add-channel! self chan)
|
||||||
|
(.subscribe client topic
|
||||||
|
(proxy [IMqttMessageListener] []
|
||||||
|
(messageArrived [topic mqtt-message]
|
||||||
|
(go (>! chan (assoc (parse-message mqtt-message)
|
||||||
|
:topic topic))))))
|
||||||
|
chan)))
|
||||||
|
|
||||||
|
(defn- parallelism []
|
||||||
|
(-> (Runtime/getRuntime)
|
||||||
|
(.availableProcessors)
|
||||||
|
(+ 1)))
|
||||||
|
|
||||||
|
(defn pipe [in xf]
|
||||||
|
(let [out (async/chan)]
|
||||||
|
(async/pipeline (parallelism) out xf in)))
|
||||||
|
|
||||||
|
(defrecord MilquetoastJsonClient [client]
|
||||||
|
IMilquetoastClient
|
||||||
|
(send-message! [_ topic msg opts]
|
||||||
|
(.publish client topic (create-message (json/write-str msg) opts)))
|
||||||
|
(stop! [_] (stop! client))
|
||||||
|
(add-channel! [_ chan] (add-channel! client chan))
|
||||||
|
(subscribe-topic! [_ topic opts]
|
||||||
|
(pipe (subscribe-topic! client topic opts)
|
||||||
|
(fn [msg] (update msg :payload json/read-str)))))
|
||||||
|
|
||||||
|
(defn send! [client topic msg & {:keys [qos retain]
|
||||||
|
:or {qos 1 retain false}}]
|
||||||
|
(send-message! client topic msg {:qos qos :retain retain}))
|
||||||
|
|
||||||
|
(defn open-channel!
|
||||||
|
[client topic & {:keys [buffer-size qos retain]
|
||||||
|
:or {buffer-size 1
|
||||||
|
qos 1
|
||||||
|
retain false}}]
|
||||||
|
(let [chan (async/chan buffer-size)]
|
||||||
|
(add-channel! client chan)
|
||||||
|
(go-loop [msg (<! chan)]
|
||||||
|
(when (not= :stop msg)
|
||||||
|
(send-message! client topic msg {:qos qos :retain retain})
|
||||||
|
(recur (<! chan))))
|
||||||
|
chan))
|
||||||
|
|
||||||
|
(defn subscribe! [client topic & {:keys [buffer-size]
|
||||||
|
:or {buffer-size 1}}]
|
||||||
|
(subscribe-topic! client topic {:buffer-size buffer-size}))
|
||||||
|
|
||||||
|
(defn connect! [& {:keys [broker-uri username password verbose]
|
||||||
|
:or {verbose false}}]
|
||||||
|
(MilquetoastClient. (create-mqtt-client! broker-uri username password)
|
||||||
|
(atom [])
|
||||||
|
verbose))
|
||||||
|
|
||||||
|
(defn connect-json! [& args]
|
||||||
|
(MilquetoastJsonClient. (apply connect! args)))
|
Loading…
Reference in New Issue