improve repl

add addr & help commands, don't restart if paused
This commit is contained in:
Norwin Roosen 2020-12-29 22:03:21 +01:00
parent c3ee33f8ce
commit 6f2e2fd666
No known key found for this signature in database
GPG Key ID: 24BC059DE24C43A3
5 changed files with 76 additions and 38 deletions

View File

@ -83,7 +83,7 @@ func main() {
// local 🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊 // local 🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊
wg.Add(1) 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 != "" { } else if *hevringAddr != "" {

View File

@ -16,7 +16,7 @@ import (
// using `conns` connections. Pixels are sent column wise, unless `shuffle` // using `conns` connections. Pixels are sent column wise, unless `shuffle`
// is set. Stops when stop is closed. // is set. Stops when stop is closed.
// @cleanup: use FlutTask{} as arg // @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 var cmds commands
if rgbsplit { if rgbsplit {
// do a RGB split of white // do a RGB split of white

View File

@ -34,10 +34,17 @@ type FlutTask struct {
MaxConns int MaxConns int
Img *image.NRGBA Img *image.NRGBA
Offset image.Point Offset image.Point
Paused bool
Shuffle bool // TODO: refactor these as RenderOpts bitfield Shuffle bool // TODO: refactor these as RenderOpts bitfield
RGBSplit bool RGBSplit bool
RandOffset 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 } type FlutAck struct{ Ok bool }
@ -54,11 +61,11 @@ func (h *Hevring) Flut(task FlutTask, reply *FlutAck) error {
close(h.taskQuit) 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.task = task
h.taskQuit = make(chan bool) 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 reply.Ok = true
return nil return nil
} }

View File

@ -109,12 +109,14 @@ func (r *Rán) applyTask(t FlutTask) {
return return
} }
r.task = t r.task = t
for _, c := range r.clients { if t.Paused {
return
}
for i, c := range r.clients {
ack := FlutAck{} ack := FlutAck{}
// @speed: should send tasks async
err := c.Call("Hevring.Flut", r.task, &ack) err := c.Call("Hevring.Flut", r.task, &ack)
if err != nil || !ack.Ok { 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 // fetch server state & sample foreign activity in image regions. assign
// subregions to clients (per connection), considering their bandwidth. // 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,
})
} }

View File

@ -21,8 +21,8 @@ type Fluter interface {
toggleMetrics() toggleMetrics()
} }
const commandMode = "CMD" const commandMode = "cmd"
const textMode = "TXT" const textMode = "txt"
// RunREPL starts reading os.Stdin for commands to apply to the given Fluter // RunREPL starts reading os.Stdin for commands to apply to the given Fluter
func RunREPL(f Fluter) { func RunREPL(f Fluter) {
@ -31,13 +31,16 @@ func RunREPL(f Fluter) {
var textCol image.Image = image.White var textCol image.Image = image.White
var bgCol image.Image = image.Transparent var bgCol image.Image = image.Transparent
fmt.Print("[rán] REPL is active. ")
printHelp()
scanner := bufio.NewScanner(os.Stdin) scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() { for scanner.Scan() {
inputStr := scanner.Text() inputStr := scanner.Text()
switch mode { switch strings.ToLower(mode) {
case textMode: case textMode:
if inputStr == commandMode { if strings.ToLower(inputStr) == commandMode {
fmt.Println("[rán] command mode") fmt.Println("[rán] command mode")
mode = commandMode mode = commandMode
continue continue
@ -50,52 +53,48 @@ func RunREPL(f Fluter) {
input := strings.Split(inputStr, " ") input := strings.Split(inputStr, " ")
cmd := strings.ToLower(input[0]) cmd := strings.ToLower(input[0])
args := input[1:] args := input[1:]
t := f.getTask()
printTask := true
switch cmd { switch cmd {
case "stop": case "stop":
t.Paused = true
f.stopTask() f.stopTask()
printTask = false
case "start": case "start":
f.applyTask(f.getTask()) t.Paused = false
case "offset": 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]) x, err := strconv.Atoi(args[0])
y, err2 := strconv.Atoi(args[1]) y, err2 := strconv.Atoi(args[1])
if err == nil && err2 == nil { if err == nil && err2 == nil {
t := f.getTask()
t.Offset = image.Pt(x, y) t.Offset = image.Pt(x, y)
f.applyTask(t)
} }
} }
case "conns": case "conns":
if len(args) == 1 { if len(args) == 1 {
if conns, err := strconv.Atoi(args[0]); err == nil { if conns, err := strconv.Atoi(args[0]); err == nil {
t := f.getTask()
t.MaxConns = conns t.MaxConns = conns
f.applyTask(t)
} }
} }
case "addr":
if len(args) == 1 {
t.Address = args[0]
}
case "shuffle": case "shuffle":
t := f.getTask()
t.Shuffle = !t.Shuffle t.Shuffle = !t.Shuffle
f.applyTask(t)
case "rgbsplit": case "rgbsplit":
t := f.getTask()
t.RGBSplit = !t.RGBSplit 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": case "txt":
if len(args) > 0 { if len(args) > 0 {
@ -110,35 +109,59 @@ func RunREPL(f Fluter) {
bgCol = parseColorOrPalette(args[2]) bgCol = parseColorOrPalette(args[2])
} }
if len(args) < 4 { 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 mode = textMode
printTask = false
} else { } else {
input := strings.Join(args[3:], " ") input := strings.Join(args[3:], " ")
t := f.getTask()
t.Img = render.RenderText(input, textSize, textCol, bgCol) t.Img = render.RenderText(input, textSize, textCol, bgCol)
f.applyTask(t)
} }
case "img": case "img":
if len(args) > 0 { if len(args) > 0 {
path := strings.Join(args, " ") path := strings.Join(args, " ")
t := f.getTask()
if img, err := render.ReadImage(path); err != nil { if img, err := render.ReadImage(path); err != nil {
fmt.Println(err) fmt.Println(err)
} else { } else {
t.Img = render.ImgToNRGBA(img) t.Img = render.ImgToNRGBA(img)
f.applyTask(t)
} }
} }
case "metrics": case "metrics":
f.toggleMetrics() 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 <n> set number of connections per client
addr <host>:<port> set target server
offset <x> <y> set top-left offset
offset rand random offset for each draw
metrics toggle bandwidth reporting (may cost some performance)
img <filepath> set image
txt <scale> <color <bgcolor> <txt> send text
txt [<scale> [<color> [<bgcolor>]] 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, // try to parse as hex-encoded RGB color,
// alternatively treat it as palette name. If both fail, // alternatively treat it as palette name. If both fail,
// give image.Transparent // give image.Transparent