Reorganized

master
noah metz 2023-12-30 16:30:23 -07:00
parent 47c93e5141
commit 508f42da34
3 changed files with 69 additions and 64 deletions

@ -0,0 +1,46 @@
package main
import (
"git.metznet.ca/MetzNet/htmxmqtt"
"errors"
"os/signal"
"syscall"
)
func main() {
handler_client, err := NewMQTTHandlerClient("tcp://localhost:1883", "", "", "htmx")
if err != nil {
panic(err)
}
handler_1, err := handler_client.NewHandler("test", PayloadFormatFunc(`<p id="test">%s</p>`))
if err != nil {
panic(err)
}
mux := http.NewServeMux()
mux.Handle("/", http.FileServer(http.Dir("./site")))
mux.Handle("/ws", handler_1)
server := &http.Server{
Handler: mux,
Addr: ":8080",
}
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGTERM, syscall.SIGKILL)
go func(sigs chan os.Signal, server *http.Server) {
<- sigs
server.Close()
}(sigs, server)
err = server.ListenAndServe()
if errors.Is(err, http.ErrServerClosed) == true {
os.Stderr.WriteString("Server closed on signal\n")
} else if err != nil {
os.Stderr.WriteString(fmt.Sprintf("Server error: %s\n", err))
} else {
os.Stderr.WriteString("Server closed\n")
}
}

@ -1,4 +1,4 @@
module git.metznet.ca/MetzNet/htmx_mqtt module git.metznet.ca/MetzNet/htmxmqtt
go 1.21.5 go 1.21.5

@ -1,12 +1,9 @@
package main package htmxmqtt
import ( import (
"errors"
"time" "time"
"net/http" "net/http"
"os" "os"
"os/signal"
"syscall"
"fmt" "fmt"
"context" "context"
"sync" "sync"
@ -19,17 +16,17 @@ import (
type MQTTFormatFunc func(mqtt.Message) []byte type MQTTFormatFunc func(mqtt.Message) []byte
type MQTTHandler struct { type MQTTHandler struct {
sync.Mutex sync.Mutex
Format MQTTFormatFunc format MQTTFormatFunc
Channels []chan mqtt.Message channels []chan mqtt.Message
} }
func (handler *MQTTHandler) ProcessMessage(client mqtt.Client, message mqtt.Message) { func (handler *MQTTHandler) processMessage(client mqtt.Client, message mqtt.Message) {
message.Ack() message.Ack()
handler.Lock() handler.Lock()
defer handler.Unlock() defer handler.Unlock()
remaining := make([]chan mqtt.Message, 0, len(handler.Channels)) remaining := make([]chan mqtt.Message, 0, len(handler.channels))
for _, channel := range(handler.Channels) { for _, channel := range(handler.channels) {
select { select {
case channel <- message: case channel <- message:
remaining = append(remaining, channel) remaining = append(remaining, channel)
@ -38,33 +35,32 @@ func (handler *MQTTHandler) ProcessMessage(client mqtt.Client, message mqtt.Mess
} }
} }
handler.Channels = remaining handler.channels = remaining
} }
func (handler *MQTTHandler) AddChannel(channel chan mqtt.Message) func() { func (handler *MQTTHandler) addChannel(channel chan mqtt.Message) func() {
handler.Lock() handler.Lock()
defer handler.Unlock() defer handler.Unlock()
handler.Channels = append(handler.Channels, channel) handler.channels = append(handler.channels, channel)
return func() { return func() {
handler.Lock() handler.Lock()
defer handler.Unlock() defer handler.Unlock()
idx := slices.Index(handler.Channels, channel) idx := slices.Index(handler.channels, channel)
if idx < 0 { if idx < 0 {
return return
} }
handler.Channels[idx] = handler.Channels[len(handler.Channels)-1] handler.channels[idx] = handler.channels[len(handler.channels)-1]
handler.Channels = handler.Channels[:len(handler.Channels)-1] handler.channels = handler.channels[:len(handler.channels)-1]
} }
} }
type MQTTHandlerClient struct { type MQTTHandlerClient struct {
mqtt.Client mqtt.Client
Subscriptions map[*MQTTHandler]string subscriptions map[*MQTTHandler]string
subscribeTimeout time.Duration
SubscribeTimeout time.Duration
} }
func NewMQTTHandlerClient(broker string, username string, password string, id string) (*MQTTHandlerClient, error) { func NewMQTTHandlerClient(broker string, username string, password string, id string) (*MQTTHandlerClient, error) {
@ -83,30 +79,30 @@ func NewMQTTHandlerClient(broker string, username string, password string, id st
return &MQTTHandlerClient{ return &MQTTHandlerClient{
Client: client, Client: client,
Subscriptions: map[*MQTTHandler]string{}, subscriptions: map[*MQTTHandler]string{},
SubscribeTimeout: 1*time.Second, subscribeTimeout: 1*time.Second,
}, nil }, nil
} }
func (client *MQTTHandlerClient) NewHandler(subscription string, format MQTTFormatFunc) (*MQTTHandler, error) { func (client *MQTTHandlerClient) NewHandler(subscription string, format MQTTFormatFunc) (*MQTTHandler, error) {
handler := &MQTTHandler{ handler := &MQTTHandler{
Format: format, format: format,
} }
sub_token := client.Subscribe(subscription, 0x00, handler.ProcessMessage) sub_token := client.Subscribe(subscription, 0x00, handler.processMessage)
timeout := sub_token.WaitTimeout(client.SubscribeTimeout) timeout := sub_token.WaitTimeout(client.subscribeTimeout)
if timeout == false || sub_token.Error() != nil { if timeout == false || sub_token.Error() != nil {
return nil, fmt.Errorf("Failed to subscribe to %s - %e", subscription, sub_token.Error()) return nil, fmt.Errorf("Failed to subscribe to %s - %e", subscription, sub_token.Error())
} }
client.Subscriptions[handler] = subscription client.subscriptions[handler] = subscription
return handler, nil return handler, nil
} }
func (handler *MQTTHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (handler *MQTTHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
channel := make(chan mqtt.Message, 1) channel := make(chan mqtt.Message, 1)
remove_channel := handler.AddChannel(channel) remove_channel := handler.addChannel(channel)
defer remove_channel() defer remove_channel()
conn, err := websocket.Accept(w, r, nil) conn, err := websocket.Accept(w, r, nil)
@ -138,7 +134,7 @@ func (handler *MQTTHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
os.Stderr.WriteString("websocket context done") os.Stderr.WriteString("websocket context done")
running = false running = false
case message := <- channel: case message := <- channel:
text := handler.Format(message) text := handler.format(message)
os.Stderr.WriteString(fmt.Sprintf("websocket write: %s\n", text)) os.Stderr.WriteString(fmt.Sprintf("websocket write: %s\n", text))
err := conn.Write(ctx, websocket.MessageText, text) err := conn.Write(ctx, websocket.MessageText, text)
if err != nil { if err != nil {
@ -154,40 +150,3 @@ func PayloadFormatFunc(template string) MQTTFormatFunc {
return []byte(fmt.Sprintf(template, message.Payload())) return []byte(fmt.Sprintf(template, message.Payload()))
} }
} }
func main() {
handler_client, err := NewMQTTHandlerClient("tcp://localhost:1883", "", "", "htmx")
if err != nil {
panic(err)
}
handler_1, err := handler_client.NewHandler("test", PayloadFormatFunc(`<p id="test">%s</p>`))
if err != nil {
panic(err)
}
mux := http.NewServeMux()
mux.Handle("/", http.FileServer(http.Dir("./site")))
mux.Handle("/ws", handler_1)
server := &http.Server{
Handler: mux,
Addr: ":8080",
}
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGTERM, syscall.SIGKILL)
go func(sigs chan os.Signal, server *http.Server) {
<- sigs
server.Close()
}(sigs, server)
err = server.ListenAndServe()
if errors.Is(err, http.ErrServerClosed) == true {
os.Stderr.WriteString("Server closed on signal\n")
} else if err != nil {
os.Stderr.WriteString(fmt.Sprintf("Server error: %s\n", err))
} else {
os.Stderr.WriteString("Server closed\n")
}
}