From 6f2e2fd66656c856933cbbfbfc9dc9f1a9495f7d Mon Sep 17 00:00:00 2001 From: Norwin Roosen Date: Tue, 29 Dec 2020 22:03:21 +0100 Subject: [PATCH] improve repl add addr & help commands, don't restart if paused --- main.go | 2 +- pixelflut/api.go | 2 +- rpc/dottir.go | 13 ++++++-- rpc/ran.go | 16 +++++++--- rpc/repl.go | 81 +++++++++++++++++++++++++++++++----------------- 5 files changed, 76 insertions(+), 38 deletions(-) diff --git a/main.go b/main.go index 8c73b2d..2420ccf 100644 --- a/main.go +++ b/main.go @@ -83,7 +83,7 @@ func main() { // local ๐ŸŒŠ๐ŸŒŠ๐ŸŒŠ๐ŸŒŠ๐ŸŒŠ๐ŸŒŠ๐ŸŒŠ๐ŸŒŠ๐ŸŒŠ๐ŸŒŠ๐ŸŒŠ๐ŸŒŠ๐ŸŒŠ๐ŸŒŠ๐ŸŒŠ wg.Add(1) - go pixelflut.Flut(img, offset, *shuffle, false, false, false, *address, *connections, stopChan, &wg) + go pixelflut.Flut(img, offset, *shuffle, false, false, *address, *connections, stopChan, &wg) } } else if *hevringAddr != "" { diff --git a/pixelflut/api.go b/pixelflut/api.go index a9a599c..6d12f5f 100644 --- a/pixelflut/api.go +++ b/pixelflut/api.go @@ -16,7 +16,7 @@ import ( // using `conns` connections. Pixels are sent column wise, unless `shuffle` // is set. Stops when stop is closed. // @cleanup: use FlutTask{} as arg -func Flut(img *image.NRGBA, position image.Point, shuffle, rgbsplit, randoffset, recreateConns bool, address string, conns int, stop chan bool, wg *sync.WaitGroup) { +func Flut(img *image.NRGBA, position image.Point, shuffle, rgbsplit, randoffset bool, address string, conns int, stop chan bool, wg *sync.WaitGroup) { var cmds commands if rgbsplit { // do a RGB split of white diff --git a/rpc/dottir.go b/rpc/dottir.go index 293825b..b69cc9f 100644 --- a/rpc/dottir.go +++ b/rpc/dottir.go @@ -34,10 +34,17 @@ type FlutTask struct { MaxConns int Img *image.NRGBA Offset image.Point + Paused bool Shuffle bool // TODO: refactor these as RenderOpts bitfield RGBSplit bool RandOffset bool - NewConn bool +} + +func (t FlutTask) String() string { + return fmt.Sprintf( + " %d conns @ %s\n img %v offset %v\n shuffle %v rgbsplit %v randoffset %v paused %v", + t.MaxConns, t.Address, t.Img.Bounds().Size(), t.Offset, t.Shuffle, t.RGBSplit, t.RandOffset, t.Paused, + ) } type FlutAck struct{ Ok bool } @@ -54,11 +61,11 @@ func (h *Hevring) Flut(task FlutTask, reply *FlutAck) error { close(h.taskQuit) } - fmt.Printf("[hevring] Rรกn gave us /w o r k/! %v\n", task) + fmt.Printf("[hevring] Rรกn gave us /w o r k/!\n%v\n", task) h.task = task h.taskQuit = make(chan bool) - go pixelflut.Flut(task.Img, task.Offset, task.Shuffle, task.RGBSplit, task.RandOffset, task.NewConn, task.Address, task.MaxConns, h.taskQuit, nil) + go pixelflut.Flut(task.Img, task.Offset, task.Shuffle, task.RGBSplit, task.RandOffset, task.Address, task.MaxConns, h.taskQuit, nil) reply.Ok = true return nil } diff --git a/rpc/ran.go b/rpc/ran.go index 979ed3a..8655051 100644 --- a/rpc/ran.go +++ b/rpc/ran.go @@ -109,12 +109,14 @@ func (r *Rรกn) applyTask(t FlutTask) { return } r.task = t - for _, c := range r.clients { + if t.Paused { + return + } + for i, c := range r.clients { ack := FlutAck{} - // @speed: should send tasks async err := c.Call("Hevring.Flut", r.task, &ack) if err != nil || !ack.Ok { - log.Printf("[rรกn] client didn't accept task") + log.Printf("[rรกn] client %d didn't accept task", i) } } } @@ -142,5 +144,11 @@ func (r *Rรกn) SetTask(img *image.NRGBA, offset image.Point, address string, max // fetch server state & sample foreign activity in image regions. assign // subregions to clients (per connection), considering their bandwidth. - r.applyTask(FlutTask{address, maxConns, img, offset, true}) + r.applyTask(FlutTask{ + Address: address, + MaxConns: maxConns, + Img: img, + Offset: offset, + Shuffle: true, + }) } diff --git a/rpc/repl.go b/rpc/repl.go index 3588305..656cfd0 100644 --- a/rpc/repl.go +++ b/rpc/repl.go @@ -21,8 +21,8 @@ type Fluter interface { toggleMetrics() } -const commandMode = "CMD" -const textMode = "TXT" +const commandMode = "cmd" +const textMode = "txt" // RunREPL starts reading os.Stdin for commands to apply to the given Fluter func RunREPL(f Fluter) { @@ -31,13 +31,16 @@ func RunREPL(f Fluter) { var textCol image.Image = image.White var bgCol image.Image = image.Transparent + fmt.Print("[rรกn] REPL is active. ") + printHelp() + scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { inputStr := scanner.Text() - switch mode { + switch strings.ToLower(mode) { case textMode: - if inputStr == commandMode { + if strings.ToLower(inputStr) == commandMode { fmt.Println("[rรกn] command mode") mode = commandMode continue @@ -50,52 +53,48 @@ func RunREPL(f Fluter) { input := strings.Split(inputStr, " ") cmd := strings.ToLower(input[0]) args := input[1:] + t := f.getTask() + printTask := true + switch cmd { case "stop": + t.Paused = true f.stopTask() + printTask = false case "start": - f.applyTask(f.getTask()) + t.Paused = false case "offset": - if len(args) == 2 { + if len(args) == 1 && args[0] == "rand" { + t.RandOffset = true + t.Offset = image.Point{} + } else if len(args) == 2 { + t.RandOffset = false x, err := strconv.Atoi(args[0]) y, err2 := strconv.Atoi(args[1]) if err == nil && err2 == nil { - t := f.getTask() t.Offset = image.Pt(x, y) - f.applyTask(t) } } case "conns": if len(args) == 1 { if conns, err := strconv.Atoi(args[0]); err == nil { - t := f.getTask() t.MaxConns = conns - f.applyTask(t) } } + case "addr": + if len(args) == 1 { + t.Address = args[0] + } + case "shuffle": - t := f.getTask() t.Shuffle = !t.Shuffle - f.applyTask(t) case "rgbsplit": - t := f.getTask() t.RGBSplit = !t.RGBSplit - f.applyTask(t) - - case "randoffset": - t := f.getTask() - t.RandOffset = !t.RandOffset - f.applyTask(t) - - case "newconn": - t := f.getTask() - t.NewConn = !t.NewConn - f.applyTask(t) case "txt": if len(args) > 0 { @@ -110,35 +109,59 @@ func RunREPL(f Fluter) { bgCol = parseColorOrPalette(args[2]) } if len(args) < 4 { - fmt.Printf("[rรกn] text mode, return via %v\n", commandMode) + fmt.Printf("[rรกn] text mode, return via '%v'\n", strings.ToUpper(commandMode)) mode = textMode + printTask = false } else { input := strings.Join(args[3:], " ") - t := f.getTask() t.Img = render.RenderText(input, textSize, textCol, bgCol) - f.applyTask(t) } case "img": if len(args) > 0 { path := strings.Join(args, " ") - t := f.getTask() if img, err := render.ReadImage(path); err != nil { fmt.Println(err) } else { t.Img = render.ImgToNRGBA(img) - f.applyTask(t) } } case "metrics": f.toggleMetrics() + printTask = false + default: + printTask = false + fmt.Print("[rรกn] unknown command. ") + printHelp() } + + if printTask { + fmt.Println(t) + } + f.applyTask(t) } } } +func printHelp() { + fmt.Println(`available commands: + start start fluting + stop pause fluting + conns set number of connections per client + addr : set target server + offset set top-left offset + offset rand random offset for each draw + metrics toggle bandwidth reporting (may cost some performance) + + img set image + txt send text + txt [ [ []] enter interactive text mode + rgbsplit toggle RGB split effect + shuffle toggle between column-wise & randomized draw order`) +} + // try to parse as hex-encoded RGB color, // alternatively treat it as palette name. If both fail, // give image.Transparent