Hochwasser/main.go

154 lines
4.0 KiB
Go
Raw Permalink Normal View History

2019-01-06 22:00:43 +01:00
package main
import (
"flag"
"fmt"
2019-01-11 17:29:00 +01:00
"image"
"log"
"math/rand"
2019-01-11 17:29:00 +01:00
"os"
"os/signal"
2019-01-06 22:00:43 +01:00
"runtime/pprof"
"sync"
"time"
"github.com/SpeckiJ/Hochwasser/pixelflut"
2020-02-14 22:29:58 +01:00
"github.com/SpeckiJ/Hochwasser/render"
"github.com/SpeckiJ/Hochwasser/rpc"
2019-01-06 22:00:43 +01:00
)
var (
imgPath = flag.String("image", "", "Filepath of an image to flut")
ránAddr = flag.String("rán", "", "Start RPC server to distribute jobs, listening on the given address/port")
hevringAddr = flag.String("hevring", "", "Connect to RPC server at given address/port")
address = flag.String("host", ":1234", "Target server address")
connections = flag.Int("connections", 4, "Number of simultaneous connections. Each connection posts a subimage")
x = flag.Int("x", 0, "Offset of posted image from left border")
y = flag.Int("y", 0, "Offset of posted image from top border")
order = flag.String("order", "rtl", "Draw order (shuffle, ltr, rtl, ttb, btt)")
fetchImgPath = flag.String("fetch", "", "Enable fetching the screen area to the given local file, updating it each second")
hevringImgPath = flag.String("hevring-preview", "", "Write the current task image to the given PNG file")
cpuprofile = flag.String("cpuprofile", "", "Destination file for CPU Profile")
)
2019-01-06 22:00:43 +01:00
func main() {
flag.Parse()
rand.Seed(time.Now().UnixNano())
task := runWithExitHandler(taskFromFlags)
2019-01-06 22:00:43 +01:00
if *cpuprofile != "" {
runWithProfiler(*cpuprofile, task)
} else {
task()
}
}
func taskFromFlags(stop chan bool, wg *sync.WaitGroup) {
rán := *ránAddr
hev := *hevringAddr
startServer := rán != "" || (hev == "" && *imgPath != "")
startClient := hev != "" || (rán == "" && *imgPath != "")
fetchImg := *fetchImgPath != ""
if !(startServer || startClient || fetchImg) {
fmt.Println("Error: At least one of the following flags is needed:\n -image -rán -hevring\n")
flag.Usage()
os.Exit(1)
2019-01-06 22:00:43 +01:00
}
2019-01-11 17:31:14 +01:00
if startServer && startClient && rán == "" && hev == "" {
rán = fmt.Sprintf(":%d", rand.Intn(30000)+1000)
hev = rán
}
if startServer {
r := rpc.SummonRán(rán, stop, wg)
var img *image.NRGBA
if *imgPath != "" {
2020-12-31 17:42:53 +01:00
var err error
if img, err = render.ReadImage(*imgPath); err != nil {
log.Fatal(err)
}
}
2020-12-31 18:44:56 +01:00
r.SetTask(pixelflut.FlutTask{
FlutTaskOpts: pixelflut.FlutTaskOpts{
Address: *address,
MaxConns: *connections,
Offset: image.Pt(*x, *y),
RenderOrder: pixelflut.NewOrder(*order),
2020-12-31 18:44:56 +01:00
},
Img: img,
})
}
if startClient {
hevring := rpc.ConnectHevring(hev, stop, wg)
hevring.PreviewPath = *hevringImgPath
}
if fetchImg {
canvasToFile(*fetchImgPath, *address, time.Second, stop, wg)
}
}
func canvasToFile(filepath, server string, interval time.Duration, stop chan bool, wg *sync.WaitGroup) {
// async fetch the image
fetchedImg := pixelflut.FetchImage(nil, server, 1, stop)
// write it in a fixed interval
go func() {
wg.Add(1)
defer wg.Done()
for loop := true; loop; {
select {
case <-stop:
loop = false
case <-time.Tick(interval):
2020-02-12 11:20:15 +01:00
}
render.WriteImage(filepath, fetchedImg)
}
}()
}
// Takes a non-blocking function, and provides it an interface for graceful shutdown:
// stop chan is closed if the routine should be stopped. before quitting, wg is awaited.
func runWithExitHandler(task func(stop chan bool, wg *sync.WaitGroup)) func() {
return func() {
wg := sync.WaitGroup{}
stopChan := make(chan bool)
interruptChan := make(chan os.Signal)
signal.Notify(interruptChan, os.Interrupt)
task(stopChan, &wg)
2020-02-12 11:20:15 +01:00
// block until we get an interrupt, or somebody says we need to quit (by closing stopChan)
select {
case <-interruptChan:
case <-stopChan:
stopChan = nil
2020-02-12 11:20:15 +01:00
}
if stopChan != nil {
// notify all async tasks to stop on interrupt, if channel wasn't closed already
close(stopChan)
}
2020-02-12 11:20:15 +01:00
// then wait for clean shutdown of all tasks before exiting
wg.Wait()
2019-01-06 22:00:43 +01:00
}
}
func runWithProfiler(outfile string, task func()) {
f, err := os.Create(outfile)
if err != nil {
log.Fatal(err)
}
defer f.Close()
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
task()
2019-01-06 22:00:43 +01:00
}