2020-07-30 18:48:48 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"html/template"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"math/rand"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/golang/glog"
|
|
|
|
|
|
|
|
mirko "code.hackerspace.pl/hscloud/go/mirko"
|
|
|
|
"code.hackerspace.pl/hscloud/hswaw/laserproxy/tpl"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
flagLaserAddress = "10.11.0.10"
|
|
|
|
flagLaserNetworkAddress = "10.11.0.1"
|
|
|
|
flagClientNetworkAddress = "10.8.1.2"
|
|
|
|
flagWebAddress = "127.0.0.1:8080"
|
2020-08-27 21:56:29 +00:00
|
|
|
flagOwnershipDuration = "1h"
|
2020-07-30 18:48:48 +00:00
|
|
|
|
|
|
|
tplIndex = template.Must(template.New("index.html").Parse(tpl.MustAssetString("index.html")))
|
|
|
|
)
|
|
|
|
|
|
|
|
type service struct {
|
|
|
|
lockCtrl chan *lockCtrl
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
flag.StringVar(&flagLaserAddress, "laser_address", flagLaserAddress, "Address of Ruida controller on laser network")
|
|
|
|
flag.StringVar(&flagLaserNetworkAddress, "laser_network", flagLaserNetworkAddress, "Address of proxy on laser network")
|
|
|
|
flag.StringVar(&flagClientNetworkAddress, "client_network", flagClientNetworkAddress, "Address of proxy on client network")
|
|
|
|
flag.StringVar(&flagWebAddress, "web_address", flagWebAddress, "Address and port to listen on for public web connections")
|
2020-08-27 21:56:29 +00:00
|
|
|
flag.StringVar(&flagOwnershipDuration, "timeout_duration", flagOwnershipDuration, "Time after which lasercutter lock will expire when not in use")
|
2020-07-30 18:48:48 +00:00
|
|
|
|
|
|
|
flag.Parse()
|
|
|
|
m := mirko.New()
|
|
|
|
if err := m.Listen(); err != nil {
|
|
|
|
glog.Exitf("Could not listen: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
lisLaser, err := net.ListenPacket("udp", fmt.Sprintf("%s:40200", flagLaserNetworkAddress))
|
|
|
|
if err != nil {
|
|
|
|
glog.Fatalf("could not listen on laser network: %v", err)
|
|
|
|
}
|
|
|
|
defer lisLaser.Close()
|
|
|
|
|
|
|
|
lisClient, err := net.ListenPacket("udp", fmt.Sprintf("%s:50200", flagClientNetworkAddress))
|
|
|
|
if err != nil {
|
|
|
|
glog.Fatalf("could not listen on client network: %v", err)
|
|
|
|
}
|
|
|
|
defer lisClient.Close()
|
|
|
|
|
|
|
|
laserIP := net.ParseIP(flagLaserAddress)
|
|
|
|
if laserIP == nil {
|
|
|
|
glog.Fatalf("could not parse laser IP address %q", flagLaserAddress)
|
|
|
|
}
|
|
|
|
laserAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:50200", laserIP.String()))
|
|
|
|
if err != nil {
|
|
|
|
glog.Fatalf("could not make laser UDP address: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-08-27 21:56:29 +00:00
|
|
|
ownershipDuration, err := time.ParseDuration(flagOwnershipDuration)
|
|
|
|
if err != nil {
|
|
|
|
glog.Fatalf("could not parse timeout duration: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-07-30 18:48:48 +00:00
|
|
|
ctx := m.Context()
|
|
|
|
s := &service{
|
|
|
|
lockCtrl: make(chan *lockCtrl),
|
|
|
|
}
|
|
|
|
updates := make(chan *lockUpdate)
|
|
|
|
go s.runProxy(ctx, updates, laserAddr, lisLaser, lisClient)
|
2020-08-27 21:56:29 +00:00
|
|
|
go s.runLocker(ctx, s.lockCtrl, ownershipDuration)
|
2020-07-30 18:48:48 +00:00
|
|
|
s.lockCtrl <- &lockCtrl{
|
|
|
|
subscribe: &lockCtrlSubscribe{
|
|
|
|
subscriber: updates,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
mux.HandleFunc("/", s.handlerIndex)
|
|
|
|
mux.HandleFunc("/take", s.handlerTake)
|
|
|
|
mux.HandleFunc("/release", s.handlerRelease)
|
|
|
|
mux.HandleFunc("/force", s.handlerForce)
|
|
|
|
httpSrv := &http.Server{Addr: flagWebAddress, Handler: mux}
|
|
|
|
|
|
|
|
glog.Infof("Listening for web connections on %q...", flagWebAddress)
|
|
|
|
go func() {
|
|
|
|
if err := httpSrv.ListenAndServe(); err != nil {
|
|
|
|
glog.Error(err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
if err := m.Serve(); err != nil {
|
|
|
|
glog.Exitf("Could not run: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
glog.Info("Running.")
|
|
|
|
<-m.Done()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
httpSrv.Shutdown(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
sampleNames = []string{
|
2023-01-05 18:55:15 +00:00
|
|
|
"elia", "radex", "qdot", "hans acker", "lars aser", "makłowicz",
|
2020-07-30 18:48:48 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
func remoteAddr(r *http.Request) string {
|
|
|
|
if fwd := r.Header.Get("X-Forwarded-For"); fwd != "" {
|
|
|
|
return strings.Split(fwd, ":")[0]
|
|
|
|
}
|
|
|
|
return strings.Split(r.RemoteAddr, ":")[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) handlerIndex(w http.ResponseWriter, r *http.Request) {
|
|
|
|
res := make(chan *lockResCurrent)
|
|
|
|
s.lockCtrl <- &lockCtrl{
|
|
|
|
getCurrent: &lockCtrlGetCurrent{
|
|
|
|
res: res,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
cur := <-res
|
|
|
|
|
|
|
|
err := tplIndex.Execute(w, struct {
|
|
|
|
You bool
|
|
|
|
CurrentAddress string
|
2020-08-27 21:56:29 +00:00
|
|
|
CurrentName string
|
2020-07-30 18:48:48 +00:00
|
|
|
CurrentDeadline string
|
|
|
|
SampleName string
|
|
|
|
}{
|
|
|
|
You: cur.addr == remoteAddr(r),
|
|
|
|
CurrentAddress: cur.addr,
|
2020-08-27 21:56:29 +00:00
|
|
|
CurrentName: cur.note,
|
2020-07-30 18:48:48 +00:00
|
|
|
CurrentDeadline: fmt.Sprintf("%d minute(s)", int(cur.deadline.Sub(time.Now()).Minutes())),
|
|
|
|
SampleName: sampleNames[rand.Intn(len(sampleNames))],
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
glog.Errorf("rendering template: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) handlerTake(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if r.Method != "POST" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
r.ParseForm()
|
|
|
|
who := r.Form.Get("who")
|
|
|
|
prev := r.Form.Get("prev")
|
|
|
|
if who == "" {
|
2020-08-27 21:56:29 +00:00
|
|
|
fmt.Fprintf(w, "excuse me, who are you? please specify a name")
|
2020-07-30 18:48:48 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
res := make(chan bool)
|
|
|
|
take := &lockCtrlTake{
|
|
|
|
note: who,
|
|
|
|
addr: remoteAddr(r),
|
|
|
|
prev: prev,
|
|
|
|
res: res,
|
|
|
|
}
|
|
|
|
s.lockCtrl <- &lockCtrl{
|
|
|
|
take: take,
|
|
|
|
}
|
|
|
|
won := <-res
|
|
|
|
|
|
|
|
if won {
|
|
|
|
http.Redirect(w, r, "/", 302)
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(w, "lock not taken")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) handlerRelease(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if r.Method != "POST" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
res := make(chan struct{})
|
|
|
|
s.lockCtrl <- &lockCtrl{
|
|
|
|
release: &lockCtrlRelease{
|
|
|
|
addr: remoteAddr(r),
|
|
|
|
res: res,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
<-res
|
|
|
|
|
|
|
|
http.Redirect(w, r, "/", 302)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *service) handlerForce(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if r.Method != "POST" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
res := make(chan struct{})
|
|
|
|
s.lockCtrl <- &lockCtrl{
|
|
|
|
release: &lockCtrlRelease{
|
|
|
|
force: true,
|
|
|
|
res: res,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
<-res
|
|
|
|
|
|
|
|
http.Redirect(w, r, "/", 302)
|
|
|
|
}
|