2022-02-27 06:57:13 +00:00
|
|
|
package pubsub
|
|
|
|
|
|
|
|
import (
|
2023-05-14 01:32:29 +00:00
|
|
|
"context"
|
2022-02-27 06:57:13 +00:00
|
|
|
"fmt"
|
2022-02-27 10:10:21 +00:00
|
|
|
"log"
|
2022-02-27 06:57:13 +00:00
|
|
|
"sort"
|
|
|
|
|
|
|
|
telemetrypb "git.wntrmute.dev/kyle/sensenet/proto"
|
|
|
|
"git.wntrmute.dev/kyle/sensenet/topic"
|
2023-05-14 01:32:29 +00:00
|
|
|
"github.com/go-zeromq/zmq4"
|
2022-02-27 06:57:13 +00:00
|
|
|
"google.golang.org/protobuf/proto"
|
|
|
|
)
|
|
|
|
|
2022-02-27 10:10:21 +00:00
|
|
|
func readTopic(b []byte) string {
|
|
|
|
size := int(b[1])
|
|
|
|
return string(b[2 : size+2])
|
|
|
|
}
|
|
|
|
|
|
|
|
func protoTopic(topic string) string {
|
|
|
|
return fmt.Sprintf("\x0a%c%s", len(topic), topic)
|
|
|
|
}
|
|
|
|
|
2022-02-27 06:57:13 +00:00
|
|
|
type Subscriber struct {
|
|
|
|
addr string
|
|
|
|
publisher string
|
2023-05-14 01:32:29 +00:00
|
|
|
sock zmq4.Socket
|
2022-02-27 06:57:13 +00:00
|
|
|
topics map[string]bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewSubscriber(addr, publisher string, topics ...string) (*Subscriber, error) {
|
|
|
|
sub := &Subscriber{
|
2022-02-27 10:10:21 +00:00
|
|
|
addr: addr,
|
|
|
|
publisher: publisher,
|
2023-05-14 01:32:29 +00:00
|
|
|
sock: zmq4.NewSub(context.Background()),
|
2022-02-27 10:10:21 +00:00
|
|
|
topics: map[string]bool{},
|
2022-02-27 06:57:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
err := sub.connect()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
sub.Conflate(1)
|
2022-02-27 10:10:21 +00:00
|
|
|
for _, topicName := range topics {
|
|
|
|
sub.Subscribe(topicName)
|
2022-02-27 06:57:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return sub, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sub *Subscriber) Conflate(n int) {
|
2023-05-14 01:32:29 +00:00
|
|
|
sub.sock.SetOption("CONFLATE", n)
|
2022-02-27 06:57:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (sub *Subscriber) connect() error {
|
2022-02-27 10:10:21 +00:00
|
|
|
log.Printf("subscriber dialing %s", sub.addr)
|
2023-05-14 01:32:29 +00:00
|
|
|
return sub.sock.Listen(sub.addr)
|
2022-02-27 06:57:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (sub *Subscriber) Subscribe(topic string) {
|
|
|
|
if sub.topics[topic] {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
sub.topics[topic] = true
|
2023-05-14 01:32:29 +00:00
|
|
|
sub.sock.SetOption(zmq4.OptionSubscribe, protoTopic(topic))
|
2022-02-27 06:57:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (sub *Subscriber) Topics() []string {
|
|
|
|
var topics = make([]string, 0, len(sub.topics))
|
|
|
|
for k := range sub.topics {
|
|
|
|
topics = append(topics, k)
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Strings(topics)
|
|
|
|
return topics
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sub *Subscriber) receive() ([]byte, error) {
|
2023-05-14 01:32:29 +00:00
|
|
|
var data []byte
|
|
|
|
|
|
|
|
msg, err := sub.sock.Recv()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, frame := range msg.Frames {
|
|
|
|
data = append(data, frame...)
|
2022-02-27 06:57:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return data, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sub *Subscriber) Receive() (*topic.Packet, error) {
|
|
|
|
data, err := sub.receive()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
pbPacket := &telemetrypb.Packet{}
|
|
|
|
err = proto.Unmarshal(data, pbPacket)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-02-27 10:10:21 +00:00
|
|
|
t := readTopic(data)
|
|
|
|
|
2022-02-27 06:57:13 +00:00
|
|
|
packet := &topic.Packet{
|
2022-02-27 10:10:21 +00:00
|
|
|
Topic: t,
|
2022-02-27 06:57:13 +00:00
|
|
|
Publisher: sub.publisher,
|
|
|
|
Received: int64(pbPacket.Timestamp),
|
|
|
|
Payload: pbPacket.Payload,
|
|
|
|
}
|
|
|
|
|
|
|
|
return packet, nil
|
|
|
|
}
|