improve repl
add addr & help commands, don't restart if paused
This commit is contained in:
		
							parent
							
								
									c3ee33f8ce
								
							
						
					
					
						commit
						6f2e2fd666
					
				
							
								
								
									
										2
									
								
								main.go
								
								
								
								
							
							
						
						
									
										2
									
								
								main.go
								
								
								
								
							| 
						 | 
					@ -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 != "" {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										16
									
								
								rpc/ran.go
								
								
								
								
							
							
						
						
									
										16
									
								
								rpc/ran.go
								
								
								
								
							| 
						 | 
					@ -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,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										83
									
								
								rpc/repl.go
								
								
								
								
							
							
						
						
									
										83
									
								
								rpc/repl.go
								
								
								
								
							| 
						 | 
					@ -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,33 +109,57 @@ 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,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue