First version
This commit is contained in:
commit
e2ef561b61
10
go.mod
Normal file
10
go.mod
Normal file
@ -0,0 +1,10 @@
|
||||
module git.quimbo.fr/nicolas/mqtt
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.2 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
)
|
12
go.sum
Normal file
12
go.sum
Normal file
@ -0,0 +1,12 @@
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.2 h1:66wOzfUHSSI1zamx7jR6yMEI5EuHnT1G6rNA5PM12m4=
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.2/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 h1:Jcxah/M+oLZ/R4/z5RzfPzGbPXnVDPkEDtf2JnuxN+U=
|
||||
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
179
mqtt.go
Normal file
179
mqtt.go
Normal file
@ -0,0 +1,179 @@
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
paho "github.com/eclipse/paho.mqtt.golang"
|
||||
)
|
||||
|
||||
// copy the paho lib Message interface
|
||||
// so no need for deps to import paho lib
|
||||
type Message interface {
|
||||
Duplicate() bool
|
||||
Qos() byte
|
||||
Retained() bool
|
||||
Topic() string
|
||||
MessageID() uint16
|
||||
Payload() []byte
|
||||
Ack()
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Host string
|
||||
Port string
|
||||
Username string
|
||||
Password string
|
||||
ClientID string
|
||||
CleanSession bool
|
||||
AutoReconnect bool
|
||||
Retained bool
|
||||
KeepAlive time.Duration
|
||||
MsgChanDept uint
|
||||
}
|
||||
|
||||
// return &Config{
|
||||
// Host: host,
|
||||
// Port: port,
|
||||
// Username: username,
|
||||
// Password: password,
|
||||
// ClientID: clientId,
|
||||
// CleanSession: true,
|
||||
// AutoReconnect: true,
|
||||
// Retained: false,
|
||||
// KeepAlive: 15 * time.Second,
|
||||
// MsgChanDept: 100,
|
||||
// }
|
||||
// }
|
||||
|
||||
type Client struct {
|
||||
Config Config
|
||||
pahoClient paho.Client
|
||||
}
|
||||
|
||||
func New(conf Config) *Client {
|
||||
opts := paho.NewClientOptions()
|
||||
|
||||
broker := conf.Host + ":" + conf.Port
|
||||
|
||||
opts.AddBroker(broker)
|
||||
opts.SetClientID(conf.ClientID)
|
||||
opts.SetUsername(conf.Username)
|
||||
opts.SetPassword(conf.Password)
|
||||
opts.SetCleanSession(conf.CleanSession)
|
||||
opts.SetAutoReconnect(conf.AutoReconnect)
|
||||
opts.SetKeepAlive(conf.KeepAlive)
|
||||
opts.SetMessageChannelDepth(conf.MsgChanDept)
|
||||
if conf.AutoReconnect {
|
||||
opts.SetResumeSubs(true)
|
||||
}
|
||||
|
||||
client := paho.NewClient(opts)
|
||||
|
||||
return &Client{
|
||||
Config: conf,
|
||||
pahoClient: client,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Connect(ctx context.Context) error {
|
||||
if token := c.pahoClient.Connect(); token.Wait() && token.Error() != nil {
|
||||
return token.Error()
|
||||
}
|
||||
|
||||
go func(ctx context.Context) {
|
||||
//TODO remove the select, only wait on signal chanel
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
c.pahoClient.Disconnect(1000)
|
||||
return
|
||||
}
|
||||
}
|
||||
}(ctx)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Subscriber struct {
|
||||
OnMessage chan Message
|
||||
OnError chan error
|
||||
}
|
||||
|
||||
func (c *Client) Subscribe(ctx context.Context, topic string, qos int) *Subscriber {
|
||||
sub := &Subscriber{
|
||||
OnMessage: make(chan Message),
|
||||
OnError: make(chan error),
|
||||
}
|
||||
|
||||
go func(ctx context.Context, sub *Subscriber) {
|
||||
defer func() {
|
||||
close(sub.OnMessage)
|
||||
close(sub.OnError)
|
||||
}()
|
||||
|
||||
if token := c.pahoClient.Subscribe(topic, byte(qos), func(mqttClient paho.Client, message paho.Message) {
|
||||
sub.OnMessage <- message
|
||||
}); token.Wait() && token.Error() != nil {
|
||||
sub.OnError <- token.Error()
|
||||
}
|
||||
|
||||
<-ctx.Done()
|
||||
}(ctx, sub)
|
||||
|
||||
return sub
|
||||
}
|
||||
|
||||
type frame struct {
|
||||
topic string
|
||||
qos int
|
||||
payload []byte
|
||||
retain bool
|
||||
}
|
||||
|
||||
type Publisher struct {
|
||||
OnError chan error
|
||||
publish chan *frame
|
||||
}
|
||||
|
||||
// Publisher will create MQTT publisher and private listener for messages to be published.
|
||||
// All messages to be published are sent through private publish channel.
|
||||
// Errors will be sent to OnError channel.
|
||||
func (c *Client) Publisher(ctx context.Context) *Publisher {
|
||||
pub := &Publisher{
|
||||
OnError: make(chan error),
|
||||
publish: make(chan *frame),
|
||||
}
|
||||
|
||||
go func(ctx context.Context, pub *Publisher) {
|
||||
defer close(pub.OnError)
|
||||
|
||||
for {
|
||||
select {
|
||||
case fr := <-pub.publish:
|
||||
if token := c.pahoClient.
|
||||
Publish(fr.topic,
|
||||
byte(fr.qos),
|
||||
fr.retain,
|
||||
fr.payload); token.Wait() && token.Error() != nil {
|
||||
pub.OnError <- token.Error()
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}(ctx, pub)
|
||||
|
||||
return pub
|
||||
}
|
||||
|
||||
// Publish message to topic through private pub.publish channel.
|
||||
// Thread-safe.
|
||||
func (pub *Publisher) Publish(topic string, qos int, message []byte, retain bool) {
|
||||
pub.publish <- &frame{
|
||||
topic: topic,
|
||||
qos: qos,
|
||||
payload: message,
|
||||
retain: retain,
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user