<commit_before>package main

import (
	"fmt"
	"os"
	"reflect"
	"runtime"

	"github.com/jessevdk/go-flags"
	"github.com/nsf/termbox-go"
	"github.com/peco/peco"
)

var version = "v0.2.8"

type cmdOptions struct {
	OptHelp           bool   `short:"h" long:"help" description:"show this help message and exit"`
	OptTTY            string `long:"tty" description:"path to the TTY (usually, the value of $TTY)"`
	OptQuery          string `long:"query" description:"initial value for query"`
	OptRcfile         string `long:"rcfile" description:"path to the settings file"`
	OptNoIgnoreCase   bool   `long:"no-ignore-case" description:"start in case-sensitive-mode (DEPRECATED)" default:"false"`
	OptVersion        bool   `long:"version" description:"print the version and exit"`
	OptBufferSize     int    `long:"buffer-size" short:"b" description:"number of lines to keep in search buffer"`
	OptEnableNullSep  bool   `long:"null" description:"expect NUL (\\0) as separator for target/output"`
	OptInitialIndex   int    `long:"initial-index" description:"position of the initial index of the selection (0 base)"`
	OptInitialMatcher string `long:"initial-matcher" description:"specify the default matcher"`
	OptPrompt         string `long:"prompt" description:"specify the prompt string"`
	OptLayout         string `long:"layout" description:"layout to be used 'top-down' (default) or 'bottom-up'"`
}

func showHelp() {
	// The ONLY reason we're not using go-flags' help option is
	// because I wanted to tweak the format just a bit... but
	// there wasn't an easy way to do so
	os.Stderr.WriteString(`
Usage: peco [options] [FILE]

Options:
`)

	t := reflect.TypeOf(cmdOptions{})
	for i := 0; i < t.NumField(); i++ {
		tag := t.Field(i).Tag

		var o string
		if s := tag.Get("short"); s != "" {
			o = fmt.Sprintf("-%s, --%s", tag.Get("short"), tag.Get("long"))
		} else {
			o = fmt.Sprintf("--%s", tag.Get("long"))
		}

		fmt.Fprintf(
			os.Stderr,
			"  %-21s %s\n",
			o,
			tag.Get("description"),
		)
	}
}

// BufferSize returns the specified buffer size. Fulfills peco.CtxOptions
func (o cmdOptions) BufferSize() int {
	return o.OptBufferSize
}

// EnableNullSep returns tru if --null was specified. Fulfills peco.CtxOptions
func (o cmdOptions) EnableNullSep() bool {
	return o.OptEnableNullSep
}

func (o cmdOptions) InitialIndex() int {
	if o.OptInitialIndex >= 0 {
		return o.OptInitialIndex + 1
	}
	return 1
}

func (o cmdOptions) LayoutType() string {
	return o.OptLayout
}

func main() {
	var err error
	var st int

	defer func() { os.Exit(st) }()

	if envvar := os.Getenv("GOMAXPROCS"); envvar == "" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}

	opts := &cmdOptions{}
	p := flags.NewParser(opts, flags.PrintErrors)
	args, err := p.Parse()
	if err != nil {
		showHelp()
		st = 1
		return
	}

	if opts.OptLayout != "" {
		if ! peco.IsValidLayoutType(peco.LayoutType(opts.OptLayout)) {
			fmt.Fprintf(os.Stderr, "Unknown layout: '%s'\n", opts.OptLayout)
			st = 1
			return
		}
	}

	if opts.OptHelp {
		showHelp()
		return
	}

	if opts.OptVersion {
		fmt.Fprintf(os.Stderr, "peco: %s\n", version)
		return
	}

	var in *os.File

	// receive in from either a file or Stdin
	switch {
	case len(args) > 0:
		in, err = os.Open(args[0])
		if err != nil {
			st = 1
			fmt.Fprintln(os.Stderr, err)
			return
		}
	case !peco.IsTty(os.Stdin.Fd()):
		in = os.Stdin
	default:
		fmt.Fprintln(os.Stderr, "You must supply something to work with via filename or stdin")
		st = 1
		return
	}

	ctx := peco.NewCtx(opts)
	defer func() {
		if err := recover(); err != nil {
			st = 1
			fmt.Fprintf(os.Stderr, "Error:\n%s", err)
		}

		if result := ctx.Result(); result != nil {
			for _, match := range result {
				line := match.Output()
				if line[len(line)-1] != '\n' {
					line = line + "\n"
				}
				fmt.Fprint(os.Stdout, line)
			}
		}
	}()

	if opts.OptRcfile == "" {
		file, err := peco.LocateRcfile()
		if err == nil {
			opts.OptRcfile = file
		}
	}

	// Default matcher is IgnoreCase
	ctx.SetCurrentMatcher(peco.IgnoreCaseMatch)

	if opts.OptRcfile != "" {
		err = ctx.ReadConfig(opts.OptRcfile)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			st = 1
			return
		}
	}

	if len(opts.OptPrompt) > 0 {
		ctx.SetPrompt(opts.OptPrompt)
	}

	// Deprecated. --no-ignore-case options will be removed in later.
	if opts.OptNoIgnoreCase {
		ctx.SetCurrentMatcher(peco.CaseSensitiveMatch)
	}

	if len(opts.OptInitialMatcher) > 0 {
		if ctx.SetCurrentMatcher(opts.OptInitialMatcher) == false {
			fmt.Fprintf(os.Stderr, "Error: Invalid matcher %s\n", opts.OptInitialMatcher)
			st = 1
			return
		}
	}

	// Try waiting for something available in the source stream
	// before doing any terminal initialization (also done by termbox)
	reader := ctx.NewBufferReader(in)
	ctx.AddWaitGroup(1)
	go reader.Loop()

	// This channel blocks until we receive something from `in`
	<-reader.InputReadyCh()

	err = peco.TtyReady()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		st = 1
		return
	}
	defer peco.TtyTerm()

	err = termbox.Init()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		st = 1
		return
	}
	defer termbox.Close()

	// Windows handle Esc/Alt self
	if runtime.GOOS == "windows" {
		termbox.SetInputMode(termbox.InputEsc | termbox.InputAlt)
	}

	view := ctx.NewView()
	filter := ctx.NewFilter()
	input := ctx.NewInput()
	sig := ctx.NewSignalHandler()

	loopers := []interface {
		Loop()
	}{
		view,
		filter,
		input,
		sig,
	}
	for _, looper := range loopers {
		ctx.AddWaitGroup(1)
		go looper.Loop()
	}

	if len(opts.OptQuery) > 0 {
		ctx.SetQuery([]rune(opts.OptQuery))
		ctx.ExecQuery()
	} else {
		view.Refresh()
	}

	ctx.WaitDone()

	st = ctx.ExitStatus()
}
<commit_msg>Fix initial-matcher error message<commit_after>package main

import (
	"fmt"
	"os"
	"reflect"
	"runtime"

	"github.com/jessevdk/go-flags"
	"github.com/nsf/termbox-go"
	"github.com/peco/peco"
)

var version = "v0.2.8"

type cmdOptions struct {
	OptHelp           bool   `short:"h" long:"help" description:"show this help message and exit"`
	OptTTY            string `long:"tty" description:"path to the TTY (usually, the value of $TTY)"`
	OptQuery          string `long:"query" description:"initial value for query"`
	OptRcfile         string `long:"rcfile" description:"path to the settings file"`
	OptNoIgnoreCase   bool   `long:"no-ignore-case" description:"start in case-sensitive-mode (DEPRECATED)" default:"false"`
	OptVersion        bool   `long:"version" description:"print the version and exit"`
	OptBufferSize     int    `long:"buffer-size" short:"b" description:"number of lines to keep in search buffer"`
	OptEnableNullSep  bool   `long:"null" description:"expect NUL (\\0) as separator for target/output"`
	OptInitialIndex   int    `long:"initial-index" description:"position of the initial index of the selection (0 base)"`
	OptInitialMatcher string `long:"initial-matcher" description:"specify the default matcher"`
	OptPrompt         string `long:"prompt" description:"specify the prompt string"`
	OptLayout         string `long:"layout" description:"layout to be used 'top-down' (default) or 'bottom-up'"`
}

func showHelp() {
	// The ONLY reason we're not using go-flags' help option is
	// because I wanted to tweak the format just a bit... but
	// there wasn't an easy way to do so
	os.Stderr.WriteString(`
Usage: peco [options] [FILE]

Options:
`)

	t := reflect.TypeOf(cmdOptions{})
	for i := 0; i < t.NumField(); i++ {
		tag := t.Field(i).Tag

		var o string
		if s := tag.Get("short"); s != "" {
			o = fmt.Sprintf("-%s, --%s", tag.Get("short"), tag.Get("long"))
		} else {
			o = fmt.Sprintf("--%s", tag.Get("long"))
		}

		fmt.Fprintf(
			os.Stderr,
			"  %-21s %s\n",
			o,
			tag.Get("description"),
		)
	}
}

// BufferSize returns the specified buffer size. Fulfills peco.CtxOptions
func (o cmdOptions) BufferSize() int {
	return o.OptBufferSize
}

// EnableNullSep returns tru if --null was specified. Fulfills peco.CtxOptions
func (o cmdOptions) EnableNullSep() bool {
	return o.OptEnableNullSep
}

func (o cmdOptions) InitialIndex() int {
	if o.OptInitialIndex >= 0 {
		return o.OptInitialIndex + 1
	}
	return 1
}

func (o cmdOptions) LayoutType() string {
	return o.OptLayout
}

func main() {
	var err error
	var st int

	defer func() { os.Exit(st) }()

	if envvar := os.Getenv("GOMAXPROCS"); envvar == "" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}

	opts := &cmdOptions{}
	p := flags.NewParser(opts, flags.PrintErrors)
	args, err := p.Parse()
	if err != nil {
		showHelp()
		st = 1
		return
	}

	if opts.OptLayout != "" {
		if ! peco.IsValidLayoutType(peco.LayoutType(opts.OptLayout)) {
			fmt.Fprintf(os.Stderr, "Unknown layout: '%s'\n", opts.OptLayout)
			st = 1
			return
		}
	}

	if opts.OptHelp {
		showHelp()
		return
	}

	if opts.OptVersion {
		fmt.Fprintf(os.Stderr, "peco: %s\n", version)
		return
	}

	var in *os.File

	// receive in from either a file or Stdin
	switch {
	case len(args) > 0:
		in, err = os.Open(args[0])
		if err != nil {
			st = 1
			fmt.Fprintln(os.Stderr, err)
			return
		}
	case !peco.IsTty(os.Stdin.Fd()):
		in = os.Stdin
	default:
		fmt.Fprintln(os.Stderr, "You must supply something to work with via filename or stdin")
		st = 1
		return
	}

	ctx := peco.NewCtx(opts)
	defer func() {
		if err := recover(); err != nil {
			st = 1
			fmt.Fprintf(os.Stderr, "Error:\n%s", err)
		}

		if result := ctx.Result(); result != nil {
			for _, match := range result {
				line := match.Output()
				if line[len(line)-1] != '\n' {
					line = line + "\n"
				}
				fmt.Fprint(os.Stdout, line)
			}
		}
	}()

	if opts.OptRcfile == "" {
		file, err := peco.LocateRcfile()
		if err == nil {
			opts.OptRcfile = file
		}
	}

	// Default matcher is IgnoreCase
	ctx.SetCurrentMatcher(peco.IgnoreCaseMatch)

	if opts.OptRcfile != "" {
		err = ctx.ReadConfig(opts.OptRcfile)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			st = 1
			return
		}
	}

	if len(opts.OptPrompt) > 0 {
		ctx.SetPrompt(opts.OptPrompt)
	}

	// Deprecated. --no-ignore-case options will be removed in later.
	if opts.OptNoIgnoreCase {
		ctx.SetCurrentMatcher(peco.CaseSensitiveMatch)
	}

	if len(opts.OptInitialMatcher) > 0 {
		ok := ctx.SetCurrentMatcher(opts.OptInitialMatcher)
		if !ok {
			fmt.Fprintf(os.Stderr, "Unknown matcher: '%s'\n", opts.OptInitialMatcher)
			st = 1
			return
		}
	}

	// Try waiting for something available in the source stream
	// before doing any terminal initialization (also done by termbox)
	reader := ctx.NewBufferReader(in)
	ctx.AddWaitGroup(1)
	go reader.Loop()

	// This channel blocks until we receive something from `in`
	<-reader.InputReadyCh()

	err = peco.TtyReady()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		st = 1
		return
	}
	defer peco.TtyTerm()

	err = termbox.Init()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		st = 1
		return
	}
	defer termbox.Close()

	// Windows handle Esc/Alt self
	if runtime.GOOS == "windows" {
		termbox.SetInputMode(termbox.InputEsc | termbox.InputAlt)
	}

	view := ctx.NewView()
	filter := ctx.NewFilter()
	input := ctx.NewInput()
	sig := ctx.NewSignalHandler()

	loopers := []interface {
		Loop()
	}{
		view,
		filter,
		input,
		sig,
	}
	for _, looper := range loopers {
		ctx.AddWaitGroup(1)
		go looper.Loop()
	}

	if len(opts.OptQuery) > 0 {
		ctx.SetQuery([]rune(opts.OptQuery))
		ctx.ExecQuery()
	} else {
		view.Refresh()
	}

	ctx.WaitDone()

	st = ctx.ExitStatus()
}
<|endoftext|>


<commit_before>package run

import (
	"fmt"
	"github.com/kballard/go-shellquote"
	"github.com/spf13/pflag"
	"io/ioutil"
	"os"
)

// *********************************************************************
// IMPORTANT:
// Usage/help docs are defined in usage.go.
// If you're updating flags, you probably need to update that file.
// *********************************************************************

type executor struct {
	cmd    string
	stdin  string
	stdout string
	stderr string
}

// flagVals captures values from CLI flag parsing
type flagVals struct {
	// Top-level flag values. These are not allowed to be redefined
	// by scattered tasks or extra args, to avoid complexity in avoiding
	// circular imports or nested scattering
	printTask    bool
	server       string
	extra        []string
	extraFiles   []string
	scatterFiles []string
	cmds         []string

	// Internal tracking of executors. Not set by flags.
	execs []executor

	// Per-task flag values. These may be overridden by scattered tasks.
	name string
	// TODO all executors share the same container and workdir
	//      but could possibly be separate.
	workdir     string
	container   string
	project     string
	description string
	stdin       []string
	stdout      []string
	stderr      []string
	preemptible bool
	wait        bool
	waitFor     []string
	inputs      []string
	inputDirs   []string
	outputs     []string
	outputDirs  []string
	contents    []string
	environ     []string
	tags        []string
	volumes     []string
	zones       []string
	cpu         int
	ram         float64
	disk        float64
}

func newFlags(v *flagVals) *pflag.FlagSet {
	f := pflag.NewFlagSet("", pflag.ContinueOnError)

	// These flags are separate because they are not allowed
	// in scattered tasks.
	//
	// Scattering and loading extra args is currently only allowed
	// at the top level in order to avoid any issues with circular
	// includes. If we want this to be per-task, it's possible,
	// but more work.
	f.StringVarP(&v.server, "server", "S", v.server, "")
	f.BoolVarP(&v.printTask, "print", "p", v.printTask, "")
	f.StringSliceVarP(&v.extra, "extra", "x", v.extra, "")
	f.StringSliceVarP(&v.extraFiles, "extra-file", "X", v.extraFiles, "")
	f.StringSliceVar(&v.scatterFiles, "scatter", v.scatterFiles, "")
	f.StringSliceVar(&v.cmds, "cmd", v.cmds, "")

	// Disable sorting in order to visit flags in primordial order below.
	// See buildExecs()
	f.SortFlags = false

	// General
	f.StringVarP(&v.container, "container", "c", v.container, "")
	f.StringVarP(&v.workdir, "workdir", "w", v.workdir, "")

	// Input/output
	f.StringSliceVarP(&v.inputs, "in", "i", v.inputs, "")
	f.StringSliceVarP(&v.inputDirs, "in-dir", "I", v.inputDirs, "")
	f.StringSliceVarP(&v.outputs, "out", "o", v.outputs, "")
	f.StringSliceVarP(&v.outputDirs, "out-dir", "O", v.outputDirs, "")
	f.StringSliceVar(&v.stdin, "stdin", v.stdin, "")
	f.StringSliceVar(&v.stdout, "stdout", v.stdout, "")
	f.StringSliceVar(&v.stderr, "stderr", v.stderr, "")
	f.StringSliceVarP(&v.contents, "contents", "C", v.contents, "")

	// Resoures
	f.IntVar(&v.cpu, "cpu", v.cpu, "")
	f.Float64Var(&v.ram, "ram", v.ram, "")
	f.Float64Var(&v.disk, "disk", v.disk, "")
	f.StringSliceVar(&v.zones, "zone", v.zones, "")
	f.BoolVar(&v.preemptible, "preemptible", v.preemptible, "")

	// Other
	f.StringVarP(&v.name, "name", "n", v.name, "")
	f.StringVar(&v.description, "description", v.description, "")
	f.StringVar(&v.project, "project", v.project, "")
	f.StringSliceVar(&v.volumes, "vol", v.volumes, "")
	f.StringSliceVar(&v.tags, "tag", v.tags, "")
	f.StringSliceVarP(&v.environ, "env", "e", v.environ, "")

	// TODO
	//f.StringVar(&cmdFile, "cmd-file", cmdFile, "Read cmd template from file")
	f.BoolVar(&v.wait, "wait", v.wait, "")
	f.StringSliceVar(&v.waitFor, "wait-for", v.waitFor, "")
	return f
}

// Set default flagVals
func defaultVals(vals *flagVals) {
	if vals.workdir == "" {
		vals.workdir = "/opt/funnel"
	}

	if vals.container == "" {
		vals.container = "alpine"
	}

	// Default name
	if vals.name == "" {
		vals.name = "Funnel run: " + vals.cmds[0]
	}

	if vals.server == "" {
		vals.server = "http://localhost:8000"
	}
}

func parseTopLevelArgs(vals *flagVals, args []string) error {
	args = loadExtras(args)
	flags := newFlags(vals)
	err := flags.Parse(args)

	if err != nil {
		return err
	}

	if len(flags.Args()) > 1 {
		return fmt.Errorf("--in, --out and --env args should have the form 'KEY=VALUE' not 'KEY VALUE'. Extra args: %s", flags.Args()[1:])
	}

	// Prepend command string given as positional argument to the args.
	// Prepend it as a flag so that it works better with parseTaskArgs().
	if len(flags.Args()) == 1 {
		cmd := flags.Args()[0]
		args = append([]string{"--cmd", cmd}, args...)
	}

	if len(vals.cmds) == 0 {
		return fmt.Errorf("you must specify a command to run")
	}

	// Fill in empty values with defaults.
	defaultVals(vals)
	parseTaskArgs(vals, args)

	return nil
}

func parseTaskArgs(vals *flagVals, args []string) {
	fl := newFlags(vals)
	fl.Parse(args)
	buildExecs(fl, vals, args)
}

// Visit flags to determine commands + stdin/out/err
// and build that information into vals.execs
func buildExecs(flags *pflag.FlagSet, vals *flagVals, args []string) {
	vals.execs = nil
	vals.cmds = nil
	var exec *executor
	flags.ParseAll(args, func(f *pflag.Flag, value string) error {
		switch f.Name {
		case "cmd":
			if exec != nil {
				// Append the current executor and start a new one.
				vals.execs = append(vals.execs, *exec)
			}
			exec = &executor{
				cmd: value,
			}
		case "stdout":
			exec.stdout = value
		case "stderr":
			exec.stderr = value
		case "stdin":
			exec.stdin = value
		}
		return nil
	})
	if exec != nil {
		vals.execs = append(vals.execs, *exec)
	}
}

// Load extra arguments from "--extra", "--extra-file", and stdin
func loadExtras(args []string) []string {
	vals := &flagVals{}
	flags := newFlags(vals)
	flags.Parse(args)

	// Load CLI arguments from files, which allows reusing common CLI args.
	for _, xf := range vals.extraFiles {
		b, _ := ioutil.ReadFile(xf)
		vals.extra = append(vals.extra, string(b))
	}

	// Load CLI arguments from stdin, which allows bash heredoc for easily
	// spreading args over multiple lines.
	stat, _ := os.Stdin.Stat()
	if (stat.Mode() & os.ModeCharDevice) == 0 {
		b, _ := ioutil.ReadAll(os.Stdin)
		if len(b) > 0 {
			vals.extra = append(vals.extra, string(b))
		}
	}

	// Load and parse all "extra" CLI arguments.
	for _, ex := range vals.extra {
		sp, _ := shellquote.Split(ex)
		args = append(args, sp...)
	}
	return args
}
<commit_msg>Fix usage printing bug in funnel run -h<commit_after>package run

import (
	"fmt"
	"github.com/kballard/go-shellquote"
	"github.com/spf13/pflag"
	"io/ioutil"
	"os"
)

// *********************************************************************
// IMPORTANT:
// Usage/help docs are defined in usage.go.
// If you're updating flags, you probably need to update that file.
// *********************************************************************

type executor struct {
	cmd    string
	stdin  string
	stdout string
	stderr string
}

// flagVals captures values from CLI flag parsing
type flagVals struct {
	// Top-level flag values. These are not allowed to be redefined
	// by scattered tasks or extra args, to avoid complexity in avoiding
	// circular imports or nested scattering
	printTask    bool
	server       string
	extra        []string
	extraFiles   []string
	scatterFiles []string
	cmds         []string

	// Internal tracking of executors. Not set by flags.
	execs []executor

	// Per-task flag values. These may be overridden by scattered tasks.
	name string
	// TODO all executors share the same container and workdir
	//      but could possibly be separate.
	workdir     string
	container   string
	project     string
	description string
	stdin       []string
	stdout      []string
	stderr      []string
	preemptible bool
	wait        bool
	waitFor     []string
	inputs      []string
	inputDirs   []string
	outputs     []string
	outputDirs  []string
	contents    []string
	environ     []string
	tags        []string
	volumes     []string
	zones       []string
	cpu         int
	ram         float64
	disk        float64
}

func newFlags(v *flagVals) *pflag.FlagSet {
	f := pflag.NewFlagSet("", pflag.ContinueOnError)
	// Disable usage because it's handled elsewhere (cmd.go)
	f.Usage = func() {}

	// These flags are separate because they are not allowed
	// in scattered tasks.
	//
	// Scattering and loading extra args is currently only allowed
	// at the top level in order to avoid any issues with circular
	// includes. If we want this to be per-task, it's possible,
	// but more work.
	f.StringVarP(&v.server, "server", "S", v.server, "")
	f.BoolVarP(&v.printTask, "print", "p", v.printTask, "")
	f.StringSliceVarP(&v.extra, "extra", "x", v.extra, "")
	f.StringSliceVarP(&v.extraFiles, "extra-file", "X", v.extraFiles, "")
	f.StringSliceVar(&v.scatterFiles, "scatter", v.scatterFiles, "")
	f.StringSliceVar(&v.cmds, "cmd", v.cmds, "")

	// Disable sorting in order to visit flags in primordial order below.
	// See buildExecs()
	f.SortFlags = false

	// General
	f.StringVarP(&v.container, "container", "c", v.container, "")
	f.StringVarP(&v.workdir, "workdir", "w", v.workdir, "")

	// Input/output
	f.StringSliceVarP(&v.inputs, "in", "i", v.inputs, "")
	f.StringSliceVarP(&v.inputDirs, "in-dir", "I", v.inputDirs, "")
	f.StringSliceVarP(&v.outputs, "out", "o", v.outputs, "")
	f.StringSliceVarP(&v.outputDirs, "out-dir", "O", v.outputDirs, "")
	f.StringSliceVar(&v.stdin, "stdin", v.stdin, "")
	f.StringSliceVar(&v.stdout, "stdout", v.stdout, "")
	f.StringSliceVar(&v.stderr, "stderr", v.stderr, "")
	f.StringSliceVarP(&v.contents, "contents", "C", v.contents, "")

	// Resoures
	f.IntVar(&v.cpu, "cpu", v.cpu, "")
	f.Float64Var(&v.ram, "ram", v.ram, "")
	f.Float64Var(&v.disk, "disk", v.disk, "")
	f.StringSliceVar(&v.zones, "zone", v.zones, "")
	f.BoolVar(&v.preemptible, "preemptible", v.preemptible, "")

	// Other
	f.StringVarP(&v.name, "name", "n", v.name, "")
	f.StringVar(&v.description, "description", v.description, "")
	f.StringVar(&v.project, "project", v.project, "")
	f.StringSliceVar(&v.volumes, "vol", v.volumes, "")
	f.StringSliceVar(&v.tags, "tag", v.tags, "")
	f.StringSliceVarP(&v.environ, "env", "e", v.environ, "")

	// TODO
	//f.StringVar(&cmdFile, "cmd-file", cmdFile, "Read cmd template from file")
	f.BoolVar(&v.wait, "wait", v.wait, "")
	f.StringSliceVar(&v.waitFor, "wait-for", v.waitFor, "")
	return f
}

// Set default flagVals
func defaultVals(vals *flagVals) {
	if vals.workdir == "" {
		vals.workdir = "/opt/funnel"
	}

	if vals.container == "" {
		vals.container = "alpine"
	}

	// Default name
	if vals.name == "" {
		vals.name = "Funnel run: " + vals.cmds[0]
	}

	if vals.server == "" {
		vals.server = "http://localhost:8000"
	}
}

func parseTopLevelArgs(vals *flagVals, args []string) error {
	args = loadExtras(args)
	flags := newFlags(vals)
	err := flags.Parse(args)

	if err != nil {
		return err
	}

	if len(flags.Args()) > 1 {
		return fmt.Errorf("--in, --out and --env args should have the form 'KEY=VALUE' not 'KEY VALUE'. Extra args: %s", flags.Args()[1:])
	}

	// Prepend command string given as positional argument to the args.
	// Prepend it as a flag so that it works better with parseTaskArgs().
	if len(flags.Args()) == 1 {
		cmd := flags.Args()[0]
		args = append([]string{"--cmd", cmd}, args...)
	}

	if len(vals.cmds) == 0 {
		return fmt.Errorf("you must specify a command to run")
	}

	// Fill in empty values with defaults.
	defaultVals(vals)
	parseTaskArgs(vals, args)

	return nil
}

func parseTaskArgs(vals *flagVals, args []string) {
	fl := newFlags(vals)
	fl.Parse(args)
	buildExecs(fl, vals, args)
}

// Visit flags to determine commands + stdin/out/err
// and build that information into vals.execs
func buildExecs(flags *pflag.FlagSet, vals *flagVals, args []string) {
	vals.execs = nil
	vals.cmds = nil
	var exec *executor
	flags.ParseAll(args, func(f *pflag.Flag, value string) error {
		switch f.Name {
		case "cmd":
			if exec != nil {
				// Append the current executor and start a new one.
				vals.execs = append(vals.execs, *exec)
			}
			exec = &executor{
				cmd: value,
			}
		case "stdout":
			exec.stdout = value
		case "stderr":
			exec.stderr = value
		case "stdin":
			exec.stdin = value
		}
		return nil
	})
	if exec != nil {
		vals.execs = append(vals.execs, *exec)
	}
}

// Load extra arguments from "--extra", "--extra-file", and stdin
func loadExtras(args []string) []string {
	vals := &flagVals{}
	flags := newFlags(vals)
	flags.Parse(args)

	// Load CLI arguments from files, which allows reusing common CLI args.
	for _, xf := range vals.extraFiles {
		b, _ := ioutil.ReadFile(xf)
		vals.extra = append(vals.extra, string(b))
	}

	// Load CLI arguments from stdin, which allows bash heredoc for easily
	// spreading args over multiple lines.
	stat, _ := os.Stdin.Stat()
	if (stat.Mode() & os.ModeCharDevice) == 0 {
		b, _ := ioutil.ReadAll(os.Stdin)
		if len(b) > 0 {
			vals.extra = append(vals.extra, string(b))
		}
	}

	// Load and parse all "extra" CLI arguments.
	for _, ex := range vals.extra {
		sp, _ := shellquote.Split(ex)
		args = append(args, sp...)
	}
	return args
}
<|endoftext|>


<commit_before>// Copyright © 2017-2018 Mikael Berthe <mikael@lilotux.net>
//
// Licensed under the MIT license.
// Please see the LICENSE file is this directory.

package cmd

import (
	"os"

	"github.com/spf13/cobra"

	"github.com/McKael/madon"
)

var timelineOpts struct {
	local          bool
	limit, keep    uint
	sinceID, maxID int64
}

// timelineCmd represents the timelines command
var timelineCmd = &cobra.Command{
	Use:     "timeline [home|public|:HASHTAG] [--local]",
	Aliases: []string{"tl"},
	Short:   "Fetch a timeline",
	Long: `
The timeline command fetches a timeline (home, local or federated).
It can also get a hashtag-based timeline if the keyword or prefixed with
':' or '#'.`,
	Example: `  madonctl timeline
  madonctl timeline public --local
  madonctl timeline :mastodon`,
	RunE:      timelineRunE,
	ValidArgs: []string{"home", "public"},
}

func init() {
	RootCmd.AddCommand(timelineCmd)

	timelineCmd.Flags().BoolVar(&timelineOpts.local, "local", false, "Posts from the local instance")
	timelineCmd.Flags().UintVarP(&timelineOpts.limit, "limit", "l", 0, "Limit number of API results")
	timelineCmd.Flags().UintVarP(&timelineOpts.keep, "keep", "k", 0, "Limit number of results")
	timelineCmd.PersistentFlags().Int64Var(&timelineOpts.sinceID, "since-id", 0, "Request IDs greater than a value")
	timelineCmd.PersistentFlags().Int64Var(&timelineOpts.maxID, "max-id", 0, "Request IDs less (or equal) than a value")
}

func timelineRunE(cmd *cobra.Command, args []string) error {
	opt := timelineOpts
	var limOpts *madon.LimitParams

	if opt.limit > 0 || opt.sinceID > 0 || opt.maxID > 0 {
		limOpts = new(madon.LimitParams)
	}

	if opt.limit > 0 {
		limOpts.Limit = int(opt.limit)
	}
	if opt.maxID > 0 {
		limOpts.MaxID = opt.maxID
	}
	if opt.sinceID > 0 {
		limOpts.SinceID = opt.sinceID
	}

	tl := "home"
	if len(args) > 0 {
		tl = args[0]
	}

	// The home timeline is the only one requiring to be logged in
	if err := madonInit(tl == "home"); err != nil {
		return err
	}

	sl, err := gClient.GetTimelines(tl, opt.local, limOpts)
	if err != nil {
		errPrint("Error: %s", err.Error())
		os.Exit(1)
	}

	if opt.keep > 0 && len(sl) > int(opt.keep) {
		sl = sl[:opt.keep]
	}

	p, err := getPrinter()
	if err != nil {
		errPrint("Error: %s", err.Error())
		os.Exit(1)
	}
	return p.printObj(sl)
}
<commit_msg>Add support for list-based timelines<commit_after>// Copyright © 2017-2018 Mikael Berthe <mikael@lilotux.net>
//
// Licensed under the MIT license.
// Please see the LICENSE file is this directory.

package cmd

import (
	"os"
	"strings"

	"github.com/spf13/cobra"

	"github.com/McKael/madon"
)

var timelineOpts struct {
	local          bool
	limit, keep    uint
	sinceID, maxID int64
}

// timelineCmd represents the timelines command
var timelineCmd = &cobra.Command{
	Use:     "timeline [home|public|:HASHTAG|!list_id] [--local]",
	Aliases: []string{"tl"},
	Short:   "Fetch a timeline",
	Long: `
The timeline command fetches a timeline (home, local or federated).
It can also get a hashtag-based timeline if the keyword or prefixed with
':' or '#', or a list-based timeline (use !ID with the list ID).`,
	Example: `  madonctl timeline
  madonctl timeline public --local
  madonctl timeline '!42'
  madonctl timeline :mastodon`,
	RunE:      timelineRunE,
	ValidArgs: []string{"home", "public"},
}

func init() {
	RootCmd.AddCommand(timelineCmd)

	timelineCmd.Flags().BoolVar(&timelineOpts.local, "local", false, "Posts from the local instance")
	timelineCmd.Flags().UintVarP(&timelineOpts.limit, "limit", "l", 0, "Limit number of API results")
	timelineCmd.Flags().UintVarP(&timelineOpts.keep, "keep", "k", 0, "Limit number of results")
	timelineCmd.PersistentFlags().Int64Var(&timelineOpts.sinceID, "since-id", 0, "Request IDs greater than a value")
	timelineCmd.PersistentFlags().Int64Var(&timelineOpts.maxID, "max-id", 0, "Request IDs less (or equal) than a value")
}

func timelineRunE(cmd *cobra.Command, args []string) error {
	opt := timelineOpts
	var limOpts *madon.LimitParams

	if opt.limit > 0 || opt.sinceID > 0 || opt.maxID > 0 {
		limOpts = new(madon.LimitParams)
	}

	if opt.limit > 0 {
		limOpts.Limit = int(opt.limit)
	}
	if opt.maxID > 0 {
		limOpts.MaxID = opt.maxID
	}
	if opt.sinceID > 0 {
		limOpts.SinceID = opt.sinceID
	}

	tl := "home"
	if len(args) > 0 {
		tl = args[0]
	}

	// Home timeline and list-based timeline require to be logged in
	if err := madonInit(tl == "home" || strings.HasPrefix(tl, "!")); err != nil {
		return err
	}

	sl, err := gClient.GetTimelines(tl, opt.local, limOpts)
	if err != nil {
		errPrint("Error: %s", err.Error())
		os.Exit(1)
	}

	if opt.keep > 0 && len(sl) > int(opt.keep) {
		sl = sl[:opt.keep]
	}

	p, err := getPrinter()
	if err != nil {
		errPrint("Error: %s", err.Error())
		os.Exit(1)
	}
	return p.printObj(sl)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"

	"github.com/go-clix/cli"
)

func prefixCommands(prefix string) (cmds []*cli.Command) {
	externalCommands, err := executablesOnPath(prefix)
	if err != nil {
		// soft fail if no commands found
		return nil
	}

	for file, path := range externalCommands {
		cmd := &cli.Command{
			Use:   fmt.Sprintf("%s --", strings.TrimPrefix(file, prefix)),
			Short: fmt.Sprintf("external command %s", path),
			Args:  cli.ArgsAny(),
		}

		ext_command := exec.Command(path)
		if ex, err := os.Executable(); err == nil {
			ext_command.Env = append(os.Environ(), fmt.Sprintf("EXECUTABLE=%s", ex))
		}
		ext_command.Stdout = os.Stdout
		ext_command.Stderr = os.Stderr

		cmd.Run = func(cmd *cli.Command, args []string) error {
			ext_command.Args = append(ext_command.Args, args...)
			return ext_command.Run()
		}
		cmds = append(cmds, cmd)
	}
	if len(cmds) > 0 {
		return cmds
	}
	return nil
}

func executablesOnPath(prefix string) (executables map[string]string, err error) {
	path, ok := os.LookupEnv("PATH")
	if !ok {
		// if PATH not set, soft fail
		return nil, fmt.Errorf("PATH not set")
	}

	paths := strings.Split(path, ":")
	for _, p := range paths {
		s, err := os.Stat(p)
		if err != nil && os.IsNotExist(err) {
			continue
		}
		if err != nil {
			return nil, err
		}
		if !s.IsDir() {
			continue
		}

		files, err := ioutil.ReadDir(p)
		if err != nil {
			return nil, err
		}
		for _, file := range files {
			if !strings.HasPrefix(file.Name(), prefix) {
				continue
			}
			if file.Mode().IsRegular() && file.Mode().Perm()&0111 != 0 {
				executables[file.Name()] = fmt.Sprintf("%s/%s", p, file.Name())
			}
		}
	}
	return executables, nil
}
<commit_msg>fix(cli): initialize executables map (#478)<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"

	"github.com/go-clix/cli"
)

func prefixCommands(prefix string) (cmds []*cli.Command) {
	externalCommands, err := executablesOnPath(prefix)
	if err != nil {
		// soft fail if no commands found
		return nil
	}

	for file, path := range externalCommands {
		cmd := &cli.Command{
			Use:   fmt.Sprintf("%s --", strings.TrimPrefix(file, prefix)),
			Short: fmt.Sprintf("external command %s", path),
			Args:  cli.ArgsAny(),
		}

		ext_command := exec.Command(path)
		if ex, err := os.Executable(); err == nil {
			ext_command.Env = append(os.Environ(), fmt.Sprintf("EXECUTABLE=%s", ex))
		}
		ext_command.Stdout = os.Stdout
		ext_command.Stderr = os.Stderr

		cmd.Run = func(cmd *cli.Command, args []string) error {
			ext_command.Args = append(ext_command.Args, args...)
			return ext_command.Run()
		}
		cmds = append(cmds, cmd)
	}
	if len(cmds) > 0 {
		return cmds
	}
	return nil
}

func executablesOnPath(prefix string) (map[string]string, error) {
	path, ok := os.LookupEnv("PATH")
	if !ok {
		// if PATH not set, soft fail
		return nil, fmt.Errorf("PATH not set")
	}

	executables := make(map[string]string)
	paths := strings.Split(path, ":")
	for _, p := range paths {
		s, err := os.Stat(p)
		if err != nil && os.IsNotExist(err) {
			continue
		}
		if err != nil {
			return nil, err
		}
		if !s.IsDir() {
			continue
		}

		files, err := ioutil.ReadDir(p)
		if err != nil {
			return nil, err
		}
		for _, file := range files {
			if !strings.HasPrefix(file.Name(), prefix) {
				continue
			}
			if file.Mode().IsRegular() && file.Mode().Perm()&0111 != 0 {
				executables[file.Name()] = fmt.Sprintf("%s/%s", p, file.Name())
			}
		}
	}
	return executables, nil
}
<|endoftext|>


<commit_before>package models

import (
	. "eaciit/wfdemo-git/library/helper"
	"time"

	"github.com/eaciit/orm"
	"gopkg.in/mgo.v2/bson"
)

type ScadaData struct {
	orm.ModelBase             `bson:"-" json:"-"`
	ID                        bson.ObjectId ` bson:"_id" json:"_id" `
	DateInfo                  DateInfo
	TimeStamp                 time.Time
	Turbine                   string
	GridFrequency             float64
	ReactivePower             float64
	AlarmExtStopTime          float64
	AlarmGridDownTime         float64
	AlarmInterLineDown        float64
	AlarmMachDownTime         float64
	AlarmOkTime               float64
	AlarmUnknownTime          float64
	AlarmWeatherStop          float64
	ExternalStopTime          float64
	GridDownTime              float64
	GridDownTimeAll           float64
	GridOkSecs                float64
	InternalLineDown          float64
	MachineDownTime           float64
	MachineDownTimeAll        float64
	OkSecs                    float64
	OkTime                    float64
	UnknownTime               float64
	UnknownTimeAll            float64
	WeatherStopTime           float64
	GeneratorRPM              float64
	NacelleYawPositionUntwist float64
	NacelleTemperature        float64
	AdjWindSpeed              float64
	AmbientTemperature        float64
	AvgBladeAngle             float64
	AvgWindSpeed              float64
	UnitsGenerated            float64
	EstimatedPower            float64
	EstimatedEnergy           float64 // new added on Sep 14, 2016 by ams
	NacelDirection            float64
	Power                     float64
	PowerLost                 float64
	Energy                    float64 // new added on Sep 14, 2016 by ams
	EnergyLost                float64 // new added on Sep 14, 2016 by ams
	RotorRPM                  float64
	WindDirection             float64
	Line                      int
	IsValidTimeDuration       bool
	TotalTime                 float64
	Minutes                   int
	ProjectName               string
	Available                 int
	DenValue                  float64 // new added on Sep 14, 2016 by ams
	DenPh                     float64 // new added on Sep 14, 2016 by ams
	DenWindSpeed              float64 // new added on Sep 14, 2016 by ams
	DenAdjWindSpeed           float64 // new added on Sep 14, 2016 by ams
	DenPower                  float64 // new added on Sep 14, 2016 by ams
	DenEnergy                 float64 // new added on Sep 14, 2016 by ams
	PCValue                   float64 // new added on Sep 15, 2016 by ams
	PCValueAdj                float64 // new added on Sep 15, 2016 by ams
	PCDeviation               float64 // new added on Sep 15, 2016 by ams
	WSAdjForPC                float64 // new added on Sep 16, 2016 by ams
	WSAvgForPC                float64 // new added on Sep 16, 2016 by ams
	TotalAvail                float64 // new added on Sep 27, 2016 by ams
	MachineAvail              float64 // new added on Sep 27, 2016 by ams
	GridAvail                 float64 // new added on Sep 27, 2016 by ams
	TotalAvailAll             float64 // new added on May 12, 2017 by fr
	MachineAvailAll           float64 // new added on May 12, 2017 by fr
	GridAvailAll              float64 // new added on May 12, 2017 by fr
	DenPcDeviation            float64 // new added on Sep 27, 2016 by ams
	DenDeviationPct           float64 // new added on Sep 27, 2016 by ams
	DenPcValue                float64 // new added on Sep 27, 2016 by ams
	DeviationPct              float64 // new added on Sep 27, 2016 by ams
	MTTR                      float64
	MTTF                      float64
	PerformanceIndex          float64
	NacelleDeviation          float64 //new added on Jul 24, 2017 by asp
	TurbineState              float64 //new added on Oct 12, 2017 by asp
	StateDescription          string  //new added on Oct 12, 2017 by asp
	StateStatus               string  //new added on Oct 12, 2017 by asp
}

func (m *ScadaData) New() *ScadaData {
	m.ID = bson.NewObjectId()
	return m
}

func (m *ScadaData) RecordID() interface{} {
	return m.ID
}

func (m *ScadaData) TableName() string {
	return "ScadaData"
}

type ScadaAlarmAnomaly struct {
	orm.ModelBase             `bson:"-",json:"-"`
	ID                        bson.ObjectId ` bson:"_id" , json:"_id" `
	DateInfo                  DateInfo
	TimeStamp                 time.Time
	Turbine                   string
	GridFrequency             float64
	ReactivePower             float64
	AlarmExtStopTime          float64
	AlarmGridDownTime         float64
	AlarmInterLineDown        float64
	AlarmMachDownTime         float64
	AlarmOkTime               float64
	AlarmUnknownTime          float64
	AlarmWeatherStop          float64
	ExternalStopTime          float64
	GridDownTime              float64
	GridOkSecs                float64
	InternalLineDown          float64
	MachineDownTime           float64
	OkSecs                    float64
	OkTime                    float64
	UnknownTime               float64
	WeatherStopTime           float64
	GeneratorRPM              float64
	NacelleYawPositionUntwist float64
	NacelleTemperature        float64
	AdjWindSpeed              float64
	AmbientTemperature        float64
	AvgBladeAngle             float64
	AvgWindSpeed              float64
	UnitsGenerated            float64
	EstimatedPower            float64
	NacelDirection            float64
	Power                     float64
	PowerLost                 float64
	RotorRPM                  float64
	WindDirection             float64
	Line                      int
	IsValidTimeDuration       bool
	TotalTime                 float64
	Minutes                   int
	ProjectName               string
	Available                 int
}

func (m *ScadaAlarmAnomaly) New() *ScadaAlarmAnomaly {
	m.ID = bson.NewObjectId()
	return m
}

func (m *ScadaAlarmAnomaly) RecordID() interface{} {
	return m.ID
}

func (m *ScadaAlarmAnomaly) TableName() string {
	return "ScadaAlarmAnomaly"
}

type ScadaClean struct {
	orm.ModelBase             `bson:"-",json:"-"`
	ID                        bson.ObjectId ` bson:"_id" , json:"_id" `
	DateInfo                  DateInfo
	TimeStamp                 time.Time
	Turbine                   string
	GridFrequency             float64
	ReactivePower             float64
	AlarmExtStopTime          float64
	AlarmGridDownTime         float64
	AlarmInterLineDown        float64
	AlarmMachDownTime         float64
	AlarmOkTime               float64
	AlarmUnknownTime          float64
	AlarmWeatherStop          float64
	ExternalStopTime          float64
	GridDownTime              float64
	GridOkSecs                float64
	InternalLineDown          float64
	MachineDownTime           float64
	OkSecs                    float64
	OkTime                    float64
	UnknownTime               float64
	WeatherStopTime           float64
	GeneratorRPM              float64
	NacelleYawPositionUntwist float64
	NacelleTemperature        float64
	AdjWindSpeed              float64
	AmbientTemperature        float64
	AvgBladeAngle             float64
	AvgWindSpeed              float64
	UnitsGenerated            float64
	EstimatedPower            float64
	NacelDirection            float64
	Power                     float64
	PowerLost                 float64
	RotorRPM                  float64
	WindDirection             float64
	Line                      int
	IsValidTimeDuration       bool
	TotalTime                 float64
	Minutes                   int
	ProjectName               string
	Available                 int
}

func (m *ScadaClean) New() *ScadaClean {
	m.ID = bson.NewObjectId()
	return m
}

func (m *ScadaClean) RecordID() interface{} {
	return m.ID
}

func (m *ScadaClean) TableName() string {
	return "ScadaClean"
}

type ScadaDataNew struct {
	orm.ModelBase             `bson:"-",json:"-"`
	ID                        bson.ObjectId ` bson:"_id" , json:"_id" `
	DateInfo                  DateInfo
	TimeStamp                 time.Time
	Turbine                   string
	GridFrequency             float64
	ReactivePower             float64
	AlarmExtStopTime          float64
	AlarmGridDownTime         float64
	AlarmInterLineDown        float64
	AlarmMachDownTime         float64
	AlarmOkTime               float64
	AlarmUnknownTime          float64
	AlarmWeatherStop          float64
	ExternalStopTime          float64
	GridDownTime              float64
	GridOkSecs                float64
	InternalLineDown          float64
	MachineDownTime           float64
	OkSecs                    float64
	OkTime                    float64
	UnknownTime               float64
	WeatherStopTime           float64
	GeneratorRPM              float64
	NacelleYawPositionUntwist float64
	NacelleTemperature        float64
	AdjWindSpeed              float64
	AmbientTemperature        float64
	AvgBladeAngle             float64
	AvgWindSpeed              float64
	UnitsGenerated            float64
	EstimatedPower            float64
	EstimatedEnergy           float64 // new added on Sep 14, 2016 by ams
	NacelDirection            float64
	Power                     float64
	PowerLost                 float64
	Energy                    float64 // new added on Sep 14, 2016 by ams
	EnergyLost                float64 // new added on Sep 14, 2016 by ams
	RotorRPM                  float64
	WindDirection             float64
	Line                      int
	IsValidTimeDuration       bool
	TotalTime                 float64
	Minutes                   int
	ProjectName               string
	Available                 int
	DenValue                  float64 // new added on Sep 14, 2016 by ams
	DenPh                     float64 // new added on Sep 14, 2016 by ams
	DenWindSpeed              float64 // new added on Sep 14, 2016 by ams
	DenAdjWindSpeed           float64 // new added on Sep 14, 2016 by ams
	DenPower                  float64 // new added on Sep 14, 2016 by ams
	DenEnergy                 float64 // new added on Sep 14, 2016 by ams
	PCValue                   float64 // new added on Sep 15, 2016 by ams
	PCDeviation               float64 // new added on Sep 15, 2016 by ams
	WSAdjForPC                float64 // new added on Sep 16, 2016 by ams
	WSAvgForPC                float64 // new added on Sep 16, 2016 by ams
}

func (m *ScadaDataNew) New() *ScadaDataNew {
	m.ID = bson.NewObjectId()
	return m
}

func (m *ScadaDataNew) RecordID() interface{} {
	return m.ID
}

func (m *ScadaDataNew) TableName() string {
	return "ScadaDataNew"
}
<commit_msg>add valid state for add condition turbine state valid<commit_after>package models

import (
	. "eaciit/wfdemo-git/library/helper"
	"time"

	"github.com/eaciit/orm"
	"gopkg.in/mgo.v2/bson"
)

type ScadaData struct {
	orm.ModelBase             `bson:"-" json:"-"`
	ID                        bson.ObjectId ` bson:"_id" json:"_id" `
	DateInfo                  DateInfo
	TimeStamp                 time.Time
	Turbine                   string
	GridFrequency             float64
	ReactivePower             float64
	AlarmExtStopTime          float64
	AlarmGridDownTime         float64
	AlarmInterLineDown        float64
	AlarmMachDownTime         float64
	AlarmOkTime               float64
	AlarmUnknownTime          float64
	AlarmWeatherStop          float64
	ExternalStopTime          float64
	GridDownTime              float64
	GridDownTimeAll           float64
	GridOkSecs                float64
	InternalLineDown          float64
	MachineDownTime           float64
	MachineDownTimeAll        float64
	OkSecs                    float64
	OkTime                    float64
	UnknownTime               float64
	UnknownTimeAll            float64
	WeatherStopTime           float64
	GeneratorRPM              float64
	NacelleYawPositionUntwist float64
	NacelleTemperature        float64
	AdjWindSpeed              float64
	AmbientTemperature        float64
	AvgBladeAngle             float64
	AvgWindSpeed              float64
	UnitsGenerated            float64
	EstimatedPower            float64
	EstimatedEnergy           float64 // new added on Sep 14, 2016 by ams
	NacelDirection            float64
	Power                     float64
	PowerLost                 float64
	Energy                    float64 // new added on Sep 14, 2016 by ams
	EnergyLost                float64 // new added on Sep 14, 2016 by ams
	RotorRPM                  float64
	WindDirection             float64
	Line                      int
	IsValidTimeDuration       bool
	TotalTime                 float64
	Minutes                   int
	ProjectName               string
	Available                 int
	DenValue                  float64 // new added on Sep 14, 2016 by ams
	DenPh                     float64 // new added on Sep 14, 2016 by ams
	DenWindSpeed              float64 // new added on Sep 14, 2016 by ams
	DenAdjWindSpeed           float64 // new added on Sep 14, 2016 by ams
	DenPower                  float64 // new added on Sep 14, 2016 by ams
	DenEnergy                 float64 // new added on Sep 14, 2016 by ams
	PCValue                   float64 // new added on Sep 15, 2016 by ams
	PCValueAdj                float64 // new added on Sep 15, 2016 by ams
	PCDeviation               float64 // new added on Sep 15, 2016 by ams
	WSAdjForPC                float64 // new added on Sep 16, 2016 by ams
	WSAvgForPC                float64 // new added on Sep 16, 2016 by ams
	TotalAvail                float64 // new added on Sep 27, 2016 by ams
	MachineAvail              float64 // new added on Sep 27, 2016 by ams
	GridAvail                 float64 // new added on Sep 27, 2016 by ams
	TotalAvailAll             float64 // new added on May 12, 2017 by fr
	MachineAvailAll           float64 // new added on May 12, 2017 by fr
	GridAvailAll              float64 // new added on May 12, 2017 by fr
	DenPcDeviation            float64 // new added on Sep 27, 2016 by ams
	DenDeviationPct           float64 // new added on Sep 27, 2016 by ams
	DenPcValue                float64 // new added on Sep 27, 2016 by ams
	DeviationPct              float64 // new added on Sep 27, 2016 by ams
	MTTR                      float64
	MTTF                      float64
	PerformanceIndex          float64
	NacelleDeviation          float64 //new added on Jul 24, 2017 by asp
	TurbineState              float64 //new added on Oct 12, 2017 by asp
	StateDescription          string  //new added on Oct 12, 2017 by asp
	StateStatus               string  //new added on Oct 12, 2017 by asp
	IsValidState              bool    //new added on Oct 17, 2017 by asp
}

func (m *ScadaData) New() *ScadaData {
	m.ID = bson.NewObjectId()
	return m
}

func (m *ScadaData) RecordID() interface{} {
	return m.ID
}

func (m *ScadaData) TableName() string {
	return "ScadaData"
}

type ScadaAlarmAnomaly struct {
	orm.ModelBase             `bson:"-",json:"-"`
	ID                        bson.ObjectId ` bson:"_id" , json:"_id" `
	DateInfo                  DateInfo
	TimeStamp                 time.Time
	Turbine                   string
	GridFrequency             float64
	ReactivePower             float64
	AlarmExtStopTime          float64
	AlarmGridDownTime         float64
	AlarmInterLineDown        float64
	AlarmMachDownTime         float64
	AlarmOkTime               float64
	AlarmUnknownTime          float64
	AlarmWeatherStop          float64
	ExternalStopTime          float64
	GridDownTime              float64
	GridOkSecs                float64
	InternalLineDown          float64
	MachineDownTime           float64
	OkSecs                    float64
	OkTime                    float64
	UnknownTime               float64
	WeatherStopTime           float64
	GeneratorRPM              float64
	NacelleYawPositionUntwist float64
	NacelleTemperature        float64
	AdjWindSpeed              float64
	AmbientTemperature        float64
	AvgBladeAngle             float64
	AvgWindSpeed              float64
	UnitsGenerated            float64
	EstimatedPower            float64
	NacelDirection            float64
	Power                     float64
	PowerLost                 float64
	RotorRPM                  float64
	WindDirection             float64
	Line                      int
	IsValidTimeDuration       bool
	TotalTime                 float64
	Minutes                   int
	ProjectName               string
	Available                 int
}

func (m *ScadaAlarmAnomaly) New() *ScadaAlarmAnomaly {
	m.ID = bson.NewObjectId()
	return m
}

func (m *ScadaAlarmAnomaly) RecordID() interface{} {
	return m.ID
}

func (m *ScadaAlarmAnomaly) TableName() string {
	return "ScadaAlarmAnomaly"
}

type ScadaClean struct {
	orm.ModelBase             `bson:"-",json:"-"`
	ID                        bson.ObjectId ` bson:"_id" , json:"_id" `
	DateInfo                  DateInfo
	TimeStamp                 time.Time
	Turbine                   string
	GridFrequency             float64
	ReactivePower             float64
	AlarmExtStopTime          float64
	AlarmGridDownTime         float64
	AlarmInterLineDown        float64
	AlarmMachDownTime         float64
	AlarmOkTime               float64
	AlarmUnknownTime          float64
	AlarmWeatherStop          float64
	ExternalStopTime          float64
	GridDownTime              float64
	GridOkSecs                float64
	InternalLineDown          float64
	MachineDownTime           float64
	OkSecs                    float64
	OkTime                    float64
	UnknownTime               float64
	WeatherStopTime           float64
	GeneratorRPM              float64
	NacelleYawPositionUntwist float64
	NacelleTemperature        float64
	AdjWindSpeed              float64
	AmbientTemperature        float64
	AvgBladeAngle             float64
	AvgWindSpeed              float64
	UnitsGenerated            float64
	EstimatedPower            float64
	NacelDirection            float64
	Power                     float64
	PowerLost                 float64
	RotorRPM                  float64
	WindDirection             float64
	Line                      int
	IsValidTimeDuration       bool
	TotalTime                 float64
	Minutes                   int
	ProjectName               string
	Available                 int
}

func (m *ScadaClean) New() *ScadaClean {
	m.ID = bson.NewObjectId()
	return m
}

func (m *ScadaClean) RecordID() interface{} {
	return m.ID
}

func (m *ScadaClean) TableName() string {
	return "ScadaClean"
}

type ScadaDataNew struct {
	orm.ModelBase             `bson:"-",json:"-"`
	ID                        bson.ObjectId ` bson:"_id" , json:"_id" `
	DateInfo                  DateInfo
	TimeStamp                 time.Time
	Turbine                   string
	GridFrequency             float64
	ReactivePower             float64
	AlarmExtStopTime          float64
	AlarmGridDownTime         float64
	AlarmInterLineDown        float64
	AlarmMachDownTime         float64
	AlarmOkTime               float64
	AlarmUnknownTime          float64
	AlarmWeatherStop          float64
	ExternalStopTime          float64
	GridDownTime              float64
	GridOkSecs                float64
	InternalLineDown          float64
	MachineDownTime           float64
	OkSecs                    float64
	OkTime                    float64
	UnknownTime               float64
	WeatherStopTime           float64
	GeneratorRPM              float64
	NacelleYawPositionUntwist float64
	NacelleTemperature        float64
	AdjWindSpeed              float64
	AmbientTemperature        float64
	AvgBladeAngle             float64
	AvgWindSpeed              float64
	UnitsGenerated            float64
	EstimatedPower            float64
	EstimatedEnergy           float64 // new added on Sep 14, 2016 by ams
	NacelDirection            float64
	Power                     float64
	PowerLost                 float64
	Energy                    float64 // new added on Sep 14, 2016 by ams
	EnergyLost                float64 // new added on Sep 14, 2016 by ams
	RotorRPM                  float64
	WindDirection             float64
	Line                      int
	IsValidTimeDuration       bool
	TotalTime                 float64
	Minutes                   int
	ProjectName               string
	Available                 int
	DenValue                  float64 // new added on Sep 14, 2016 by ams
	DenPh                     float64 // new added on Sep 14, 2016 by ams
	DenWindSpeed              float64 // new added on Sep 14, 2016 by ams
	DenAdjWindSpeed           float64 // new added on Sep 14, 2016 by ams
	DenPower                  float64 // new added on Sep 14, 2016 by ams
	DenEnergy                 float64 // new added on Sep 14, 2016 by ams
	PCValue                   float64 // new added on Sep 15, 2016 by ams
	PCDeviation               float64 // new added on Sep 15, 2016 by ams
	WSAdjForPC                float64 // new added on Sep 16, 2016 by ams
	WSAvgForPC                float64 // new added on Sep 16, 2016 by ams
}

func (m *ScadaDataNew) New() *ScadaDataNew {
	m.ID = bson.NewObjectId()
	return m
}

func (m *ScadaDataNew) RecordID() interface{} {
	return m.ID
}

func (m *ScadaDataNew) TableName() string {
	return "ScadaDataNew"
}
<|endoftext|>


<commit_before>// +build cgo

/*
Copyright 2014 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package midi

import (
	"log"
	"sync/atomic"

	"github.com/rakyll/portmidi"
)

func initMidi() {
	device := portmidi.DeviceId(*midiDevice)
	if device == -1 {
		device = portmidi.GetDefaultInputDeviceId()
	}
	s, err := portmidi.NewInputStream(device, 1024)
	if err != nil {
		log.Println(err)
		return
	}
	if s == nil {
		log.Println("could not initialize MIDI input device")
		return
	}
	go midiLoop(s)
}

func midiLoop(s *portmidi.Stream) {
	noteOn := make([]int64, 0, 128)
	for e := range s.Listen() {
		switch e.Status {
		case 144: // note on
			on := false
			for _, n := range noteOn {
				if n == e.Data1 {
					on = true
				}
			}
			if !on {
				noteOn = append(noteOn, e.Data1)
			}
			atomic.StoreInt64(&midiNote, e.Data1)
			atomic.StoreInt64(&midiGate, 1)
		case 128: // note off
			for i, n := range noteOn {
				if n == e.Data1 {
					copy(noteOn[i:], noteOn[i+1:])
					noteOn = noteOn[:len(noteOn)-1]
				}
			}
			if len(noteOn) > 0 {
				n := noteOn[len(noteOn)-1]
				atomic.StoreInt64(&midiNote, n)
			} else {
				atomic.StoreInt64(&midiGate, 0)
			}
		}
	}
}
<commit_msg>Updated to work with PortMidi<commit_after>// +build cgo

/*
Copyright 2014 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package midi

import (
	"log"
	"sync/atomic"

	"github.com/rakyll/portmidi"
)

func initMidi() {
	device := portmidi.DeviceID(*midiDevice)
	if device == -1 {
		device = portmidi.DefaultInputDeviceID()
	}
	s, err := portmidi.NewInputStream(device, 1024)
	if err != nil {
		log.Println(err)
		return
	}
	if s == nil {
		log.Println("could not initialize MIDI input device")
		return
	}
	go midiLoop(s)
}

func midiLoop(s *portmidi.Stream) {
	noteOn := make([]int64, 0, 128)
	for e := range s.Listen() {
		switch e.Status {
		case 144: // note on
			on := false
			for _, n := range noteOn {
				if n == e.Data1 {
					on = true
				}
			}
			if !on {
				noteOn = append(noteOn, e.Data1)
			}
			atomic.StoreInt64(&midiNote, e.Data1)
			atomic.StoreInt64(&midiGate, 1)
		case 128: // note off
			for i, n := range noteOn {
				if n == e.Data1 {
					copy(noteOn[i:], noteOn[i+1:])
					noteOn = noteOn[:len(noteOn)-1]
				}
			}
			if len(noteOn) > 0 {
				n := noteOn[len(noteOn)-1]
				atomic.StoreInt64(&midiNote, n)
			} else {
				atomic.StoreInt64(&midiGate, 0)
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package migrate

import (
	"fmt"
	"log"
	"os"
	"path"

	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
	"github.com/coreos/etcd/pkg/pbutil"
	"github.com/coreos/etcd/pkg/types"
	raftpb "github.com/coreos/etcd/raft/raftpb"
	"github.com/coreos/etcd/snap"
	"github.com/coreos/etcd/wal"
	"github.com/coreos/etcd/wal/walpb"
)

const termOffset4to2 = 1

func snapDir4(dataDir string) string {
	return path.Join(dataDir, "snapshot")
}

func logFile4(dataDir string) string {
	return path.Join(dataDir, "log")
}

func cfgFile4(dataDir string) string {
	return path.Join(dataDir, "conf")
}

func snapDir2(dataDir string) string {
	return path.Join(dataDir, "snap")
}

func walDir2(dataDir string) string {
	return path.Join(dataDir, "wal")
}

func Migrate4To2(dataDir string, name string) error {
	// prep new directories
	sd2 := snapDir2(dataDir)
	if err := os.MkdirAll(sd2, 0700); err != nil {
		return fmt.Errorf("failed creating snapshot directory %s: %v", sd2, err)
	}

	// read v0.4 data
	snap4, err := DecodeLatestSnapshot4FromDir(snapDir4(dataDir))
	if err != nil {
		return err
	}

	cfg4, err := DecodeConfig4FromFile(cfgFile4(dataDir))
	if err != nil {
		return err
	}

	ents4, err := DecodeLog4FromFile(logFile4(dataDir))
	if err != nil {
		return err
	}

	nodeIDs := ents4.NodeIDs()
	nodeID := GuessNodeID(nodeIDs, snap4, cfg4, name)

	if nodeID == 0 {
		return fmt.Errorf("Couldn't figure out the node ID from the log or flags, cannot convert")
	}

	metadata := pbutil.MustMarshal(&pb.Metadata{NodeID: nodeID, ClusterID: 0x04add5})
	wd2 := walDir2(dataDir)
	w, err := wal.Create(wd2, metadata)
	if err != nil {
		return fmt.Errorf("failed initializing wal at %s: %v", wd2, err)
	}
	defer w.Close()

	// transform v0.4 data
	var snap2 *raftpb.Snapshot
	if snap4 == nil {
		log.Printf("No snapshot found")
	} else {
		log.Printf("Found snapshot: lastIndex=%d", snap4.LastIndex)

		snap2 = snap4.Snapshot2()
	}

	st2 := cfg4.HardState2()

	// If we've got the most recent snapshot, we can use it's committed index. Still likely less than the current actual index, but worth it for the replay.
	if snap2 != nil {
		st2.Commit = snap2.Metadata.Index
	}

	ents2, err := Entries4To2(ents4)
	if err != nil {
		return err
	}

	ents2Len := len(ents2)
	log.Printf("Found %d log entries: firstIndex=%d lastIndex=%d", ents2Len, ents2[0].Index, ents2[ents2Len-1].Index)

	// explicitly prepend an empty entry as the WAL code expects it
	ents2 = append(make([]raftpb.Entry, 1), ents2...)

	if err = w.Save(st2, ents2); err != nil {
		return err
	}
	log.Printf("Log migration successful")

	// migrate snapshot (if necessary) and logs
	var walsnap walpb.Snapshot
	if snap2 != nil {
		walsnap.Index, walsnap.Term = snap2.Metadata.Index, snap2.Metadata.Term
		ss := snap.New(sd2)
		if err := ss.SaveSnap(*snap2); err != nil {
			return err
		}
		log.Printf("Snapshot migration successful")
	}
	if err = w.SaveSnapshot(walsnap); err != nil {
		return err
	}

	return nil
}

func GuessNodeID(nodes map[string]uint64, snap4 *Snapshot4, cfg *Config4, name string) uint64 {
	var snapNodes map[string]uint64
	if snap4 != nil {
		snapNodes = snap4.GetNodesFromStore()
	}
	// First, use the flag, if set.
	if name != "" {
		log.Printf("Using suggested name %s", name)
		if val, ok := nodes[name]; ok {
			log.Printf("Assigning %s the ID %s", name, types.ID(val))
			return val
		}
		if snapNodes != nil {
			if val, ok := snapNodes[name]; ok {
				log.Printf("Assigning %s the ID %s", name, types.ID(val))
				return val
			}
		}
		log.Printf("Name not found, autodetecting...")
	}
	// Next, look at the snapshot peers, if that exists.
	if snap4 != nil {
		//snapNodes := make(map[string]uint64)
		//for _, p := range snap4.Peers {
		//m := generateNodeMember(p.Name, p.ConnectionString, "")
		//snapNodes[p.Name] = uint64(m.ID)
		//}
		for _, p := range cfg.Peers {
			delete(snapNodes, p.Name)
		}
		if len(snapNodes) == 1 {
			for name, id := range nodes {
				log.Printf("Autodetected from snapshot: name %s", name)
				return id
			}
		}
	}
	// Then, try and deduce from the log.
	for _, p := range cfg.Peers {
		delete(nodes, p.Name)
	}
	if len(nodes) == 1 {
		for name, id := range nodes {
			log.Printf("Autodetected name %s", name)
			return id
		}
	}
	return 0
}
<commit_msg>comment<commit_after>// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package migrate

import (
	"fmt"
	"log"
	"os"
	"path"

	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
	"github.com/coreos/etcd/pkg/pbutil"
	"github.com/coreos/etcd/pkg/types"
	raftpb "github.com/coreos/etcd/raft/raftpb"
	"github.com/coreos/etcd/snap"
	"github.com/coreos/etcd/wal"
	"github.com/coreos/etcd/wal/walpb"
)

// We need an offset in leader election terms, because term 0 is special in 2.0.
const termOffset4to2 = 1

func snapDir4(dataDir string) string {
	return path.Join(dataDir, "snapshot")
}

func logFile4(dataDir string) string {
	return path.Join(dataDir, "log")
}

func cfgFile4(dataDir string) string {
	return path.Join(dataDir, "conf")
}

func snapDir2(dataDir string) string {
	return path.Join(dataDir, "snap")
}

func walDir2(dataDir string) string {
	return path.Join(dataDir, "wal")
}

func Migrate4To2(dataDir string, name string) error {
	// prep new directories
	sd2 := snapDir2(dataDir)
	if err := os.MkdirAll(sd2, 0700); err != nil {
		return fmt.Errorf("failed creating snapshot directory %s: %v", sd2, err)
	}

	// read v0.4 data
	snap4, err := DecodeLatestSnapshot4FromDir(snapDir4(dataDir))
	if err != nil {
		return err
	}

	cfg4, err := DecodeConfig4FromFile(cfgFile4(dataDir))
	if err != nil {
		return err
	}

	ents4, err := DecodeLog4FromFile(logFile4(dataDir))
	if err != nil {
		return err
	}

	nodeIDs := ents4.NodeIDs()
	nodeID := GuessNodeID(nodeIDs, snap4, cfg4, name)

	if nodeID == 0 {
		return fmt.Errorf("Couldn't figure out the node ID from the log or flags, cannot convert")
	}

	metadata := pbutil.MustMarshal(&pb.Metadata{NodeID: nodeID, ClusterID: 0x04add5})
	wd2 := walDir2(dataDir)
	w, err := wal.Create(wd2, metadata)
	if err != nil {
		return fmt.Errorf("failed initializing wal at %s: %v", wd2, err)
	}
	defer w.Close()

	// transform v0.4 data
	var snap2 *raftpb.Snapshot
	if snap4 == nil {
		log.Printf("No snapshot found")
	} else {
		log.Printf("Found snapshot: lastIndex=%d", snap4.LastIndex)

		snap2 = snap4.Snapshot2()
	}

	st2 := cfg4.HardState2()

	// If we've got the most recent snapshot, we can use it's committed index. Still likely less than the current actual index, but worth it for the replay.
	if snap2 != nil {
		st2.Commit = snap2.Metadata.Index
	}

	ents2, err := Entries4To2(ents4)
	if err != nil {
		return err
	}

	ents2Len := len(ents2)
	log.Printf("Found %d log entries: firstIndex=%d lastIndex=%d", ents2Len, ents2[0].Index, ents2[ents2Len-1].Index)

	// explicitly prepend an empty entry as the WAL code expects it
	ents2 = append(make([]raftpb.Entry, 1), ents2...)

	if err = w.Save(st2, ents2); err != nil {
		return err
	}
	log.Printf("Log migration successful")

	// migrate snapshot (if necessary) and logs
	var walsnap walpb.Snapshot
	if snap2 != nil {
		walsnap.Index, walsnap.Term = snap2.Metadata.Index, snap2.Metadata.Term
		ss := snap.New(sd2)
		if err := ss.SaveSnap(*snap2); err != nil {
			return err
		}
		log.Printf("Snapshot migration successful")
	}
	if err = w.SaveSnapshot(walsnap); err != nil {
		return err
	}

	return nil
}

func GuessNodeID(nodes map[string]uint64, snap4 *Snapshot4, cfg *Config4, name string) uint64 {
	var snapNodes map[string]uint64
	if snap4 != nil {
		snapNodes = snap4.GetNodesFromStore()
	}
	// First, use the flag, if set.
	if name != "" {
		log.Printf("Using suggested name %s", name)
		if val, ok := nodes[name]; ok {
			log.Printf("Assigning %s the ID %s", name, types.ID(val))
			return val
		}
		if snapNodes != nil {
			if val, ok := snapNodes[name]; ok {
				log.Printf("Assigning %s the ID %s", name, types.ID(val))
				return val
			}
		}
		log.Printf("Name not found, autodetecting...")
	}
	// Next, look at the snapshot peers, if that exists.
	if snap4 != nil {
		//snapNodes := make(map[string]uint64)
		//for _, p := range snap4.Peers {
		//m := generateNodeMember(p.Name, p.ConnectionString, "")
		//snapNodes[p.Name] = uint64(m.ID)
		//}
		for _, p := range cfg.Peers {
			delete(snapNodes, p.Name)
		}
		if len(snapNodes) == 1 {
			for name, id := range nodes {
				log.Printf("Autodetected from snapshot: name %s", name)
				return id
			}
		}
	}
	// Then, try and deduce from the log.
	for _, p := range cfg.Peers {
		delete(nodes, p.Name)
	}
	if len(nodes) == 1 {
		for name, id := range nodes {
			log.Printf("Autodetected name %s", name)
			return id
		}
	}
	return 0
}
<|endoftext|>


<commit_before>package pool

import (
	"container/heap"
	"math"
	"math/rand"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"

	"github.com/weaveworks/flux/balancer/model"
)

const (
	retry_initial_interval  = 1
	retry_backoff_factor    = 4
	retry_abandon_threshold = 256 // ~4min
)

type InstancePool interface {
	ReactivateRetries(t time.Time)
	UpdateInstances(instances []model.Instance)
	PickActiveInstance() PooledInstance
	PickInstance() PooledInstance
}

type PooledInstance interface {
	Instance() *model.Instance
	Keep()
	Fail()
}

type poolEntry struct {
	instance      *model.Instance
	pool          *instancePool
	retryInterval int
}

type retryEntry struct {
	*poolEntry
	retryTime time.Time
}

type retryQueue struct {
	retries []*retryEntry
}

type instancePool struct {
	members map[string]struct{}
	active  []*poolEntry
	retry   *retryQueue
	lock    sync.Mutex
}

func NewInstancePool() InstancePool {
	pool := &instancePool{
		members: make(map[string]struct{}),
		retry:   &retryQueue{},
	}
	heap.Init(pool.retry)
	return pool
}

// Make any instances that are due for a retry available again
func (p *instancePool) ReactivateRetries(t time.Time) {
	p.lock.Lock()
	defer p.lock.Unlock()
	for p.retry.beforeOrAt(t) {
		entry := p.retry.take1()
		if entry.retryInterval < retry_abandon_threshold {
			log.Infof("Giving instance %s another chance", entry.instance.Name)
			entry.retryInterval *= retry_backoff_factor
			p.active = append(p.active, entry)
		} else {
			delete(p.members, entry.instance.Name)
			log.Infof("Abandoning instance %s after %d retries",
				entry.instance.Name,
				1+int(math.Log(float64(entry.retryInterval))/
					math.Log(float64(retry_backoff_factor))))
		}
	}
}

func (p *instancePool) UpdateInstances(instances []model.Instance) {
	p.lock.Lock()
	defer p.lock.Unlock()
	newActive := []*poolEntry{}
	remainder := p.members
	p.members = map[string]struct{}{}

	for i, inst := range instances {
		p.members[inst.Name] = struct{}{}
		if _, found := remainder[inst.Name]; found {
			delete(remainder, inst.Name)
		} else {
			newActive = append(newActive, &poolEntry{
				pool:          p,
				instance:      &instances[i],
				retryInterval: retry_initial_interval,
			})
		}
	}
	p.removeMembers(remainder)
	p.active = append(p.active, newActive...)
}

func (p *instancePool) removeMembers(names map[string]struct{}) {
	newActive := []*poolEntry{}
	for _, entry := range p.active {
		if _, found := names[entry.instance.Name]; !found {
			newActive = append(newActive, entry)
		}
	}
	p.active = newActive

	newRetries := []*retryEntry{}
	for _, entry := range p.retry.retries {
		if _, found := names[entry.instance.Name]; !found {
			newRetries = append(newRetries, entry)
		}
	}
	p.retry.retries = newRetries
	heap.Init(p.retry)
}

// Pick an instance from amongst the active instances; return nil if
// there are none.
func (p *instancePool) PickActiveInstance() PooledInstance {
	p.lock.Lock()
	defer p.lock.Unlock()
	return p.pickActiveInstance()
}

func (p *instancePool) pickActiveInstance() PooledInstance {
	n := len(p.active)
	if n > 0 {
		return p.active[rand.Intn(n)]
	}
	return nil
}

// Pick an instance from the pool; ideally, from amongst the active
// instances, but failing that, from those waiting to be retried.
func (p *instancePool) PickInstance() PooledInstance {
	p.lock.Lock()
	defer p.lock.Unlock()

	// NB it is an invariant that the instance returned must be
	// present in the set of active instances, so that if `Keep` is
	// called, it does not need to be (conditionally) moved.
	inst := p.pickActiveInstance()
	if inst != nil {
		return inst
	}
	// Ruh-roh, no active instances. Raid the retry queue.
	if p.retry.Len() > 0 {
		entry := p.retry.take1()
		p.active = []*poolEntry{entry}
		return entry
	}
	return nil
}

func (entry *poolEntry) Keep() {
	entry.retryInterval = retry_initial_interval
}

func (entry *poolEntry) Fail() {
	log.Infof("Scheduling instance %s for retry in %d sec", entry.instance.Name, entry.retryInterval)
	p := entry.pool

	p.lock.Lock()
	defer p.lock.Unlock()

	for i, e := range p.active {
		if e == entry {
			p.active = append(p.active[0:i], p.active[i+1:]...)
			p.retry.scheduleRetry(entry, time.Now().Add(time.Duration(entry.retryInterval)*time.Second))
			return
		}
	}
}

func (entry *poolEntry) Instance() *model.Instance {
	return entry.instance
}

// =====

// heap.Interface
func (q *retryQueue) Len() int {
	return len(q.retries)
}

func (q *retryQueue) Less(i, j int) bool {
	return q.retries[i].retryTime.Before(q.retries[j].retryTime)
}

func (q *retryQueue) Swap(i, j int) {
	q.retries[i], q.retries[j] = q.retries[j], q.retries[i]
}

func (q *retryQueue) Push(r interface{}) {
	q.retries = append(q.retries, r.(*retryEntry))
}

func (q *retryQueue) Pop() interface{} {
	last := len(q.retries) - 1
	r := q.retries[last]
	q.retries = q.retries[0:last]
	return r
}

// End heap.Interface

func (q *retryQueue) beforeOrAt(t time.Time) bool {
	if len(q.retries) == 0 {
		return false
	}
	return !q.retries[len(q.retries)-1].retryTime.After(t)
}

func (q *retryQueue) take1() *poolEntry {
	return heap.Pop(q).(*retryEntry).poolEntry
}

func (q *retryQueue) scheduleRetry(entry *poolEntry, t time.Time) {
	r := &retryEntry{entry, t}
	heap.Push(q, r)
}
<commit_msg>Use a private RNG in instancePool<commit_after>package pool

import (
	"container/heap"
	"math"
	"math/rand"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"

	"github.com/weaveworks/flux/balancer/model"
)

const (
	retry_initial_interval  = 1
	retry_backoff_factor    = 4
	retry_abandon_threshold = 256 // ~4min
)

type InstancePool interface {
	ReactivateRetries(t time.Time)
	UpdateInstances(instances []model.Instance)
	PickActiveInstance() PooledInstance
	PickInstance() PooledInstance
}

type PooledInstance interface {
	Instance() *model.Instance
	Keep()
	Fail()
}

type poolEntry struct {
	instance      *model.Instance
	pool          *instancePool
	retryInterval int
}

type retryEntry struct {
	*poolEntry
	retryTime time.Time
}

type retryQueue struct {
	retries []*retryEntry
}

type instancePool struct {
	members map[string]struct{}
	active  []*poolEntry
	retry   *retryQueue
	lock    sync.Mutex
	rng     *rand.Rand
}

func NewInstancePool() InstancePool {
	pool := &instancePool{
		members: make(map[string]struct{}),
		retry:   &retryQueue{},
		rng:     rand.New(rand.NewSource(time.Now().UnixNano())),
	}
	heap.Init(pool.retry)
	return pool
}

// Make any instances that are due for a retry available again
func (p *instancePool) ReactivateRetries(t time.Time) {
	p.lock.Lock()
	defer p.lock.Unlock()
	for p.retry.beforeOrAt(t) {
		entry := p.retry.take1()
		if entry.retryInterval < retry_abandon_threshold {
			log.Infof("Giving instance %s another chance", entry.instance.Name)
			entry.retryInterval *= retry_backoff_factor
			p.active = append(p.active, entry)
		} else {
			delete(p.members, entry.instance.Name)
			log.Infof("Abandoning instance %s after %d retries",
				entry.instance.Name,
				1+int(math.Log(float64(entry.retryInterval))/
					math.Log(float64(retry_backoff_factor))))
		}
	}
}

func (p *instancePool) UpdateInstances(instances []model.Instance) {
	p.lock.Lock()
	defer p.lock.Unlock()
	newActive := []*poolEntry{}
	remainder := p.members
	p.members = map[string]struct{}{}

	for i, inst := range instances {
		p.members[inst.Name] = struct{}{}
		if _, found := remainder[inst.Name]; found {
			delete(remainder, inst.Name)
		} else {
			newActive = append(newActive, &poolEntry{
				pool:          p,
				instance:      &instances[i],
				retryInterval: retry_initial_interval,
			})
		}
	}
	p.removeMembers(remainder)
	p.active = append(p.active, newActive...)
}

func (p *instancePool) removeMembers(names map[string]struct{}) {
	newActive := []*poolEntry{}
	for _, entry := range p.active {
		if _, found := names[entry.instance.Name]; !found {
			newActive = append(newActive, entry)
		}
	}
	p.active = newActive

	newRetries := []*retryEntry{}
	for _, entry := range p.retry.retries {
		if _, found := names[entry.instance.Name]; !found {
			newRetries = append(newRetries, entry)
		}
	}
	p.retry.retries = newRetries
	heap.Init(p.retry)
}

// Pick an instance from amongst the active instances; return nil if
// there are none.
func (p *instancePool) PickActiveInstance() PooledInstance {
	p.lock.Lock()
	defer p.lock.Unlock()
	return p.pickActiveInstance()
}

func (p *instancePool) pickActiveInstance() PooledInstance {
	n := len(p.active)
	if n > 0 {
		return p.active[p.rng.Intn(n)]
	}
	return nil
}

// Pick an instance from the pool; ideally, from amongst the active
// instances, but failing that, from those waiting to be retried.
func (p *instancePool) PickInstance() PooledInstance {
	p.lock.Lock()
	defer p.lock.Unlock()

	// NB it is an invariant that the instance returned must be
	// present in the set of active instances, so that if `Keep` is
	// called, it does not need to be (conditionally) moved.
	inst := p.pickActiveInstance()
	if inst != nil {
		return inst
	}
	// Ruh-roh, no active instances. Raid the retry queue.
	if p.retry.Len() > 0 {
		entry := p.retry.take1()
		p.active = []*poolEntry{entry}
		return entry
	}
	return nil
}

func (entry *poolEntry) Keep() {
	entry.retryInterval = retry_initial_interval
}

func (entry *poolEntry) Fail() {
	log.Infof("Scheduling instance %s for retry in %d sec", entry.instance.Name, entry.retryInterval)
	p := entry.pool

	p.lock.Lock()
	defer p.lock.Unlock()

	for i, e := range p.active {
		if e == entry {
			p.active = append(p.active[0:i], p.active[i+1:]...)
			p.retry.scheduleRetry(entry, time.Now().Add(time.Duration(entry.retryInterval)*time.Second))
			return
		}
	}
}

func (entry *poolEntry) Instance() *model.Instance {
	return entry.instance
}

// =====

// heap.Interface
func (q *retryQueue) Len() int {
	return len(q.retries)
}

func (q *retryQueue) Less(i, j int) bool {
	return q.retries[i].retryTime.Before(q.retries[j].retryTime)
}

func (q *retryQueue) Swap(i, j int) {
	q.retries[i], q.retries[j] = q.retries[j], q.retries[i]
}

func (q *retryQueue) Push(r interface{}) {
	q.retries = append(q.retries, r.(*retryEntry))
}

func (q *retryQueue) Pop() interface{} {
	last := len(q.retries) - 1
	r := q.retries[last]
	q.retries = q.retries[0:last]
	return r
}

// End heap.Interface

func (q *retryQueue) beforeOrAt(t time.Time) bool {
	if len(q.retries) == 0 {
		return false
	}
	return !q.retries[len(q.retries)-1].retryTime.After(t)
}

func (q *retryQueue) take1() *poolEntry {
	return heap.Pop(q).(*retryEntry).poolEntry
}

func (q *retryQueue) scheduleRetry(entry *poolEntry, t time.Time) {
	r := &retryEntry{entry, t}
	heap.Push(q, r)
}
<|endoftext|>


<commit_before>package libvirt

import (
	"encoding/xml"
	"fmt"
	"log"
	"strings"

	libvirt "github.com/digitalocean/go-libvirt"
	libvirtxml "github.com/libvirt/libvirt-go-xml"
)

func getGuestForArchType(caps libvirtxml.Caps, arch string, virttype string) (libvirtxml.CapsGuest, error) {
	for _, guest := range caps.Guests {
		log.Printf("[TRACE] Checking for %s/%s against %s/%s\n", arch, virttype, guest.Arch.Name, guest.OSType)
		if guest.Arch.Name == arch && guest.OSType == virttype {
			log.Printf("[DEBUG] Found %d machines in guest for %s/%s", len(guest.Arch.Machines), arch, virttype)
			return guest, nil
		}
	}
	return libvirtxml.CapsGuest{}, fmt.Errorf("[DEBUG] Could not find any guests for architecure type %s/%s", virttype, arch)
}

func lookupMachine(machines []libvirtxml.CapsGuestMachine, targetmachine string) string {
	for _, machine := range machines {
		if machine.Name == targetmachine {
			if machine.Canonical != "" {
				return machine.Canonical
			}
			return machine.Name
		}
	}
	return ""
}

func getCanonicalMachineName(caps libvirtxml.Caps, arch string, virttype string, targetmachine string) (string, error) {
	guest, err := getGuestForArchType(caps, arch, virttype)
	if err != nil {
		return "", err
	}

	/* Machine entries can be in the guest.Arch.Machines level as well as
	   under each guest.Arch.Domains[].Machines */

	name := lookupMachine(guest.Arch.Machines, targetmachine)
	if name != "" {
		return name, nil
	}

	for _, domain := range guest.Arch.Domains {
		name := lookupMachine(domain.Machines, targetmachine)
		if name != "" {
			return name, nil
		}
	}

	return "", fmt.Errorf("[WARN] Cannot find machine type %s for %s/%s in %v", targetmachine, virttype, arch, caps)
}

func getOriginalMachineName(caps libvirtxml.Caps, arch string, virttype string, targetmachine string) (string, error) {
	guest, err := getGuestForArchType(caps, arch, virttype)
	if err != nil {
		return "", err
	}

	for _, machine := range guest.Arch.Machines {
		if machine.Canonical != "" && machine.Canonical == targetmachine {
			return machine.Name, nil
		}
	}
	return targetmachine, nil // There wasn't a canonical mapping to this
}

// as kernal args allow duplicate keys, we use a list of maps
// we jump to a next map as soon as we find a duplicate
// key
func splitKernelCmdLine(cmdLine string) ([]map[string]string, error) {
	var cmdLines []map[string]string
	if len(cmdLine) == 0 {
		return cmdLines, nil
	}

	currCmdLine := make(map[string]string)
	keylessCmdLineArgs := []string{}

	argVals := strings.Split(cmdLine, " ")
	for _, argVal := range argVals {
		if !strings.Contains(argVal, "=") {
			// keyless cmd line (eg: nosplash)
			keylessCmdLineArgs = append(keylessCmdLineArgs, argVal)
			continue
		}

		kv := strings.SplitN(argVal, "=", 2)
		k, v := kv[0], kv[1]
		// if the key is duplicate, start a new map
		if _, ok := currCmdLine[k]; ok {
			cmdLines = append(cmdLines, currCmdLine)
			currCmdLine = make(map[string]string)
		}
		currCmdLine[k] = v
	}
	if len(currCmdLine) > 0 {
		cmdLines = append(cmdLines, currCmdLine)
	}
	if len(keylessCmdLineArgs) > 0 {
		cl := make(map[string]string)
		cl["_"] = strings.Join(keylessCmdLineArgs, " ")
		cmdLines = append(cmdLines, cl)
	}
	return cmdLines, nil
}

func getHostArchitecture(virConn *libvirt.Libvirt) (string, error) {
	type HostCapabilities struct {
		XMLName xml.Name `xml:"capabilities"`
		Host    struct {
			XMLName xml.Name `xml:"host"`
			CPU     struct {
				XMLName xml.Name `xml:"cpu"`
				Arch    string   `xml:"arch"`
			}
		}
	}

	info, err := virConn.Capabilities()
	if err != nil {
		return "", err
	}

	capabilities := HostCapabilities{}
	err = xml.Unmarshal([]byte(info), &capabilities)
	if err != nil {
		return "", err
	}

	return capabilities.Host.CPU.Arch, nil
}

//
func getHostCapabilities(virConn *libvirt.Libvirt) (libvirtxml.Caps, error) {
	// We should perhaps think of storing this on the connect object
	// on first call to avoid the back and forth
	caps := libvirtxml.Caps{}
	capsXML, err := virConn.Capabilities()
	if err != nil {
		return caps, err
	}
	xml.Unmarshal([]byte(capsXML), &caps)
	log.Printf("[TRACE] Capabilities of host \n %+v", caps)
	return caps, nil
}
<commit_msg>Error return value of  is not checked<commit_after>package libvirt

import (
	"encoding/xml"
	"fmt"
	"log"
	"strings"

	libvirt "github.com/digitalocean/go-libvirt"
	libvirtxml "github.com/libvirt/libvirt-go-xml"
)

func getGuestForArchType(caps libvirtxml.Caps, arch string, virttype string) (libvirtxml.CapsGuest, error) {
	for _, guest := range caps.Guests {
		log.Printf("[TRACE] Checking for %s/%s against %s/%s\n", arch, virttype, guest.Arch.Name, guest.OSType)
		if guest.Arch.Name == arch && guest.OSType == virttype {
			log.Printf("[DEBUG] Found %d machines in guest for %s/%s", len(guest.Arch.Machines), arch, virttype)
			return guest, nil
		}
	}
	return libvirtxml.CapsGuest{}, fmt.Errorf("[DEBUG] Could not find any guests for architecure type %s/%s", virttype, arch)
}

func lookupMachine(machines []libvirtxml.CapsGuestMachine, targetmachine string) string {
	for _, machine := range machines {
		if machine.Name == targetmachine {
			if machine.Canonical != "" {
				return machine.Canonical
			}
			return machine.Name
		}
	}
	return ""
}

func getCanonicalMachineName(caps libvirtxml.Caps, arch string, virttype string, targetmachine string) (string, error) {
	guest, err := getGuestForArchType(caps, arch, virttype)
	if err != nil {
		return "", err
	}

	/* Machine entries can be in the guest.Arch.Machines level as well as
	   under each guest.Arch.Domains[].Machines */

	name := lookupMachine(guest.Arch.Machines, targetmachine)
	if name != "" {
		return name, nil
	}

	for _, domain := range guest.Arch.Domains {
		name := lookupMachine(domain.Machines, targetmachine)
		if name != "" {
			return name, nil
		}
	}

	return "", fmt.Errorf("[WARN] Cannot find machine type %s for %s/%s in %v", targetmachine, virttype, arch, caps)
}

func getOriginalMachineName(caps libvirtxml.Caps, arch string, virttype string, targetmachine string) (string, error) {
	guest, err := getGuestForArchType(caps, arch, virttype)
	if err != nil {
		return "", err
	}

	for _, machine := range guest.Arch.Machines {
		if machine.Canonical != "" && machine.Canonical == targetmachine {
			return machine.Name, nil
		}
	}
	return targetmachine, nil // There wasn't a canonical mapping to this
}

// as kernal args allow duplicate keys, we use a list of maps
// we jump to a next map as soon as we find a duplicate
// key
func splitKernelCmdLine(cmdLine string) ([]map[string]string, error) {
	var cmdLines []map[string]string
	if len(cmdLine) == 0 {
		return cmdLines, nil
	}

	currCmdLine := make(map[string]string)
	keylessCmdLineArgs := []string{}

	argVals := strings.Split(cmdLine, " ")
	for _, argVal := range argVals {
		if !strings.Contains(argVal, "=") {
			// keyless cmd line (eg: nosplash)
			keylessCmdLineArgs = append(keylessCmdLineArgs, argVal)
			continue
		}

		kv := strings.SplitN(argVal, "=", 2)
		k, v := kv[0], kv[1]
		// if the key is duplicate, start a new map
		if _, ok := currCmdLine[k]; ok {
			cmdLines = append(cmdLines, currCmdLine)
			currCmdLine = make(map[string]string)
		}
		currCmdLine[k] = v
	}
	if len(currCmdLine) > 0 {
		cmdLines = append(cmdLines, currCmdLine)
	}
	if len(keylessCmdLineArgs) > 0 {
		cl := make(map[string]string)
		cl["_"] = strings.Join(keylessCmdLineArgs, " ")
		cmdLines = append(cmdLines, cl)
	}
	return cmdLines, nil
}

func getHostArchitecture(virConn *libvirt.Libvirt) (string, error) {
	type HostCapabilities struct {
		XMLName xml.Name `xml:"capabilities"`
		Host    struct {
			XMLName xml.Name `xml:"host"`
			CPU     struct {
				XMLName xml.Name `xml:"cpu"`
				Arch    string   `xml:"arch"`
			}
		}
	}

	info, err := virConn.Capabilities()
	if err != nil {
		return "", err
	}

	capabilities := HostCapabilities{}
	err = xml.Unmarshal([]byte(info), &capabilities)
	if err != nil {
		return "", err
	}

	return capabilities.Host.CPU.Arch, nil
}

//
func getHostCapabilities(virConn *libvirt.Libvirt) (libvirtxml.Caps, error) {
	// We should perhaps think of storing this on the connect object
	// on first call to avoid the back and forth
	caps := libvirtxml.Caps{}
	capsXML, err := virConn.Capabilities()
	if err != nil {
		return caps, err
	}

	err = xml.Unmarshal([]byte(capsXML), &caps)
	if err != nil {
		return caps, err
	}

	log.Printf("[TRACE] Capabilities of host \n %+v", caps)
	return caps, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package block

import (
	"errors"
	"time"

	"github.com/m3db/m3db/context"
	"github.com/m3db/m3db/digest"
	"github.com/m3db/m3db/ts"
	xio "github.com/m3db/m3db/x/io"
)

var (
	errReadFromClosedBlock = errors.New("attempt to read from a closed block")
	errWriteToClosedBlock  = errors.New("attempt to write to a closed block")

	timeZero = time.Time{}
)

// NB(xichen): locking of dbBlock instances is currently done outside the dbBlock struct at the series level.
// Specifically, read lock is acquired for accessing operations like Stream(), and write lock is acquired
// for mutating operations like Write(), Reset(), and Close(). Adding a explicit lock to the dbBlock struct might
// make it more clear w.r.t. how/when we acquire locks, though.
type dbBlock struct {
	opts     Options
	ctx      context.Context
	start    time.Time
	segment  ts.Segment
	checksum uint32
	closed   bool
}

// NewDatabaseBlock creates a new DatabaseBlock instance.
func NewDatabaseBlock(start time.Time, segment ts.Segment, opts Options) DatabaseBlock {
	b := &dbBlock{
		opts:   opts,
		ctx:    opts.ContextPool().Get(),
		start:  start,
		closed: false,
	}
	b.resetSegment(segment)
	return b
}

func (b *dbBlock) StartTime() time.Time {
	return b.start
}

func (b *dbBlock) Checksum() *uint32 {
	cksum := b.checksum
	return &cksum
}

func (b *dbBlock) Stream(blocker context.Context) (xio.SegmentReader, error) {
	if b.closed {
		return nil, errReadFromClosedBlock
	}
	b.ctx.DependsOn(blocker)
	// If the block is not writable, and the segment is empty, it means
	// there are no data encoded in this block, so we return a nil reader.
	if b.segment.Head == nil && b.segment.Tail == nil {
		return nil, nil
	}
	s := b.opts.SegmentReaderPool().Get()
	s.Reset(b.segment)
	blocker.RegisterFinalizer(context.FinalizerFn(s.Close))
	return s, nil
}

// Reset resets the block start time and the encoder.
func (b *dbBlock) Reset(start time.Time, segment ts.Segment) {
	if !b.closed {
		b.ctx.Close()
	}
	b.ctx = b.opts.ContextPool().Get()
	b.start = start
	b.closed = false
	b.resetSegment(segment)
}

func (b *dbBlock) resetSegment(seg ts.Segment) {
	b.segment = seg
	b.checksum = digest.SegmentChecksum(seg)

	b.ctx.RegisterFinalizer(&seg)
}

func (b *dbBlock) Close() {
	if b.closed {
		return
	}

	b.closed = true
	b.ctx.Close()

	if pool := b.opts.DatabaseBlockPool(); pool != nil {
		pool.Put(b)
	}
}

type databaseSeriesBlocks struct {
	opts  Options
	elems map[time.Time]DatabaseBlock
	min   time.Time
	max   time.Time
}

// NewDatabaseSeriesBlocks creates a databaseSeriesBlocks instance.
func NewDatabaseSeriesBlocks(capacity int, opts Options) DatabaseSeriesBlocks {
	return &databaseSeriesBlocks{
		opts:  opts,
		elems: make(map[time.Time]DatabaseBlock, capacity),
	}
}

func (dbb *databaseSeriesBlocks) Options() Options {
	return dbb.opts
}

func (dbb *databaseSeriesBlocks) Len() int {
	return len(dbb.elems)
}

func (dbb *databaseSeriesBlocks) AddBlock(block DatabaseBlock) {
	start := block.StartTime()
	if dbb.min.Equal(timeZero) || start.Before(dbb.min) {
		dbb.min = start
	}
	if dbb.max.Equal(timeZero) || start.After(dbb.max) {
		dbb.max = start
	}
	dbb.elems[start] = block
}

func (dbb *databaseSeriesBlocks) AddSeries(other DatabaseSeriesBlocks) {
	if other == nil {
		return
	}
	blocks := other.AllBlocks()
	for _, b := range blocks {
		dbb.AddBlock(b)
	}
}

// MinTime returns the min time of the blocks contained.
func (dbb *databaseSeriesBlocks) MinTime() time.Time {
	return dbb.min
}

// MaxTime returns the max time of the blocks contained.
func (dbb *databaseSeriesBlocks) MaxTime() time.Time {
	return dbb.max
}

func (dbb *databaseSeriesBlocks) BlockAt(t time.Time) (DatabaseBlock, bool) {
	b, ok := dbb.elems[t]
	return b, ok
}

func (dbb *databaseSeriesBlocks) AllBlocks() map[time.Time]DatabaseBlock {
	return dbb.elems
}

func (dbb *databaseSeriesBlocks) RemoveBlockAt(t time.Time) {
	if _, exists := dbb.elems[t]; !exists {
		return
	}
	delete(dbb.elems, t)
	if !dbb.min.Equal(t) && !dbb.max.Equal(t) {
		return
	}
	dbb.min, dbb.max = timeZero, timeZero
	if len(dbb.elems) == 0 {
		return
	}
	for k := range dbb.elems {
		if dbb.min == timeZero || dbb.min.After(k) {
			dbb.min = k
		}
		if dbb.max == timeZero || dbb.max.Before(k) {
			dbb.max = k
		}
	}
}

func (dbb *databaseSeriesBlocks) RemoveAll() {
	for t, block := range dbb.elems {
		block.Close()
		delete(dbb.elems, t)
	}
}

func (dbb *databaseSeriesBlocks) Close() {
	dbb.RemoveAll()
}
<commit_msg>Avoid returning block byte slices to pool from SegmentReader (#197)<commit_after>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package block

import (
	"errors"
	"time"

	"github.com/m3db/m3db/context"
	"github.com/m3db/m3db/digest"
	"github.com/m3db/m3db/ts"
	xio "github.com/m3db/m3db/x/io"
)

var (
	errReadFromClosedBlock = errors.New("attempt to read from a closed block")
	errWriteToClosedBlock  = errors.New("attempt to write to a closed block")

	timeZero = time.Time{}
)

// NB(xichen): locking of dbBlock instances is currently done outside the dbBlock struct at the series level.
// Specifically, read lock is acquired for accessing operations like Stream(), and write lock is acquired
// for mutating operations like Write(), Reset(), and Close(). Adding a explicit lock to the dbBlock struct might
// make it more clear w.r.t. how/when we acquire locks, though.
type dbBlock struct {
	opts     Options
	ctx      context.Context
	start    time.Time
	segment  ts.Segment
	checksum uint32
	closed   bool
}

// NewDatabaseBlock creates a new DatabaseBlock instance.
func NewDatabaseBlock(start time.Time, segment ts.Segment, opts Options) DatabaseBlock {
	b := &dbBlock{
		opts:   opts,
		ctx:    opts.ContextPool().Get(),
		start:  start,
		closed: false,
	}
	b.resetSegment(segment)
	return b
}

func (b *dbBlock) StartTime() time.Time {
	return b.start
}

func (b *dbBlock) Checksum() *uint32 {
	cksum := b.checksum
	return &cksum
}

func (b *dbBlock) Stream(blocker context.Context) (xio.SegmentReader, error) {
	if b.closed {
		return nil, errReadFromClosedBlock
	}
	b.ctx.DependsOn(blocker)
	// If the block is not writable, and the segment is empty, it means
	// there are no data encoded in this block, so we return a nil reader.
	if b.segment.Head == nil && b.segment.Tail == nil {
		return nil, nil
	}
	s := b.opts.SegmentReaderPool().Get()
	// NB(r): We explicitly pass a new segment without a HeadPool to avoid
	// the segment reader returning these byte slices to the pool again as these
	// are immutable slices that are shared amongst all the readers.
	s.Reset(ts.Segment{
		Head: b.segment.Head,
		Tail: b.segment.Tail,
	})
	blocker.RegisterFinalizer(context.FinalizerFn(s.Close))
	return s, nil
}

// Reset resets the block start time and the encoder.
func (b *dbBlock) Reset(start time.Time, segment ts.Segment) {
	if !b.closed {
		b.ctx.Close()
	}
	b.ctx = b.opts.ContextPool().Get()
	b.start = start
	b.closed = false
	b.resetSegment(segment)
}

func (b *dbBlock) resetSegment(seg ts.Segment) {
	b.segment = seg
	b.checksum = digest.SegmentChecksum(seg)

	b.ctx.RegisterFinalizer(&seg)
}

func (b *dbBlock) Close() {
	if b.closed {
		return
	}

	b.closed = true
	b.ctx.Close()

	if pool := b.opts.DatabaseBlockPool(); pool != nil {
		pool.Put(b)
	}
}

type databaseSeriesBlocks struct {
	opts  Options
	elems map[time.Time]DatabaseBlock
	min   time.Time
	max   time.Time
}

// NewDatabaseSeriesBlocks creates a databaseSeriesBlocks instance.
func NewDatabaseSeriesBlocks(capacity int, opts Options) DatabaseSeriesBlocks {
	return &databaseSeriesBlocks{
		opts:  opts,
		elems: make(map[time.Time]DatabaseBlock, capacity),
	}
}

func (dbb *databaseSeriesBlocks) Options() Options {
	return dbb.opts
}

func (dbb *databaseSeriesBlocks) Len() int {
	return len(dbb.elems)
}

func (dbb *databaseSeriesBlocks) AddBlock(block DatabaseBlock) {
	start := block.StartTime()
	if dbb.min.Equal(timeZero) || start.Before(dbb.min) {
		dbb.min = start
	}
	if dbb.max.Equal(timeZero) || start.After(dbb.max) {
		dbb.max = start
	}
	dbb.elems[start] = block
}

func (dbb *databaseSeriesBlocks) AddSeries(other DatabaseSeriesBlocks) {
	if other == nil {
		return
	}
	blocks := other.AllBlocks()
	for _, b := range blocks {
		dbb.AddBlock(b)
	}
}

// MinTime returns the min time of the blocks contained.
func (dbb *databaseSeriesBlocks) MinTime() time.Time {
	return dbb.min
}

// MaxTime returns the max time of the blocks contained.
func (dbb *databaseSeriesBlocks) MaxTime() time.Time {
	return dbb.max
}

func (dbb *databaseSeriesBlocks) BlockAt(t time.Time) (DatabaseBlock, bool) {
	b, ok := dbb.elems[t]
	return b, ok
}

func (dbb *databaseSeriesBlocks) AllBlocks() map[time.Time]DatabaseBlock {
	return dbb.elems
}

func (dbb *databaseSeriesBlocks) RemoveBlockAt(t time.Time) {
	if _, exists := dbb.elems[t]; !exists {
		return
	}
	delete(dbb.elems, t)
	if !dbb.min.Equal(t) && !dbb.max.Equal(t) {
		return
	}
	dbb.min, dbb.max = timeZero, timeZero
	if len(dbb.elems) == 0 {
		return
	}
	for k := range dbb.elems {
		if dbb.min == timeZero || dbb.min.After(k) {
			dbb.min = k
		}
		if dbb.max == timeZero || dbb.max.Before(k) {
			dbb.max = k
		}
	}
}

func (dbb *databaseSeriesBlocks) RemoveAll() {
	for t, block := range dbb.elems {
		block.Close()
		delete(dbb.elems, t)
	}
}

func (dbb *databaseSeriesBlocks) Close() {
	dbb.RemoveAll()
}
<|endoftext|>