<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package addresser_test

import (
	"sort"
	"sync"

	gc "gopkg.in/check.v1"

	"github.com/juju/errors"
	"github.com/juju/names"
	"github.com/juju/testing"
	jujutxn "github.com/juju/txn"

	"github.com/juju/juju/apiserver/addresser"
	"github.com/juju/juju/environs/config"
	"github.com/juju/juju/state"
)

// mockState implements StateInterface and allows inspection of called
// methods.
type mockState struct {
	mu   sync.Mutex
	stub *testing.Stub

	config      *config.Config
	ipAddresses map[string]*mockIPAddress

	configWatchers    []*mockConfigWatcher
	ipAddressWatchers []*mockIPAddressWatcher
}

func newMockState() *mockState {
	mst := &mockState{
		stub:        &testing.Stub{},
		ipAddresses: make(map[string]*mockIPAddress),
	}
	mst.setUpIPAddresses()
	return mst
}

var _ addresser.StateInterface = (*mockState)(nil)

// EnvironConfig implements StateInterface.
func (mst *mockState) EnvironConfig() (*config.Config, error) {
	mst.mu.Lock()
	defer mst.mu.Unlock()

	mst.stub.MethodCall(mst, "EnvironConfig")

	if err := mst.stub.NextErr(); err != nil {
		return nil, err
	}
	return mst.config, nil
}

// setConfig updates the environ config stored internally. Triggers a
// change event for all created config watchers.
func (mst *mockState) setConfig(c *gc.C, newConfig *config.Config) {
	mst.mu.Lock()
	defer mst.mu.Unlock()

	mst.config = newConfig

	// Notify any watchers for the changes.
	for _, w := range mst.configWatchers {
		w.incoming <- struct{}{}
	}
}

// WatchForEnvironConfigChanges implements StateInterface.
func (mst *mockState) WatchForEnvironConfigChanges() state.NotifyWatcher {
	mst.mu.Lock()
	defer mst.mu.Unlock()

	mst.stub.MethodCall(mst, "WatchForEnvironConfigChanges")
	mst.stub.NextErr()
	return mst.nextEnvironConfigChangesWatcher()
}

// addEnvironConfigChangesWatcher adds an environment config changes watches.
func (mst *mockState) addEnvironConfigChangesWatcher(err error) *mockConfigWatcher {
	w := newMockConfigWatcher(err)
	mst.configWatchers = append(mst.configWatchers, w)
	return w
}

// nextEnvironConfigChangesWatcher returns an environment
// config changes watcher.
func (mst *mockState) nextEnvironConfigChangesWatcher() *mockConfigWatcher {
	if len(mst.configWatchers) == 0 {
		panic("ran out of watchers")
	}

	w := mst.configWatchers[0]
	mst.configWatchers = mst.configWatchers[1:]
	w.start()
	return w
}

// FindEntity implements StateInterface.
func (mst *mockState) FindEntity(tag names.Tag) (state.Entity, error) {
	mst.mu.Lock()
	defer mst.mu.Unlock()

	mst.stub.MethodCall(mst, "FindEntity", tag)

	if err := mst.stub.NextErr(); err != nil {
		return nil, err
	}
	if tag == nil {
		return nil, errors.NotValidf("nil tag is") // +" not valid"
	}
	for _, ipAddress := range mst.ipAddresses {
		if ipAddress.Tag().String() == tag.String() {
			return ipAddress, nil
		}
	}
	return nil, errors.NotFoundf("IP address %s", tag)
}

// IPAddress implements StateInterface.
func (mst *mockState) IPAddress(value string) (addresser.StateIPAddress, error) {
	mst.mu.Lock()
	defer mst.mu.Unlock()

	mst.stub.MethodCall(mst, "IPAddress", value)

	if err := mst.stub.NextErr(); err != nil {
		return nil, err
	}
	ipAddress, found := mst.ipAddresses[value]
	if !found {
		return nil, errors.NotFoundf("IP address %s", value)
	}
	return ipAddress, nil
}

// WatchIPAddresses implements StateInterface.
func (mst *mockState) WatchIPAddresses() state.StringsWatcher {
	mst.mu.Lock()
	defer mst.mu.Unlock()

	mst.stub.MethodCall(mst, "WatchIPAddresses")
	mst.stub.NextErr()
	return mst.nextIPAddressWatcher()
}

// addIPAddressWatcher adds an IP address watcher with the given IDs.
func (mst *mockState) addIPAddressWatcher(ids ...string) *mockIPAddressWatcher {
	w := newMockIPAddressWatcher(ids)
	mst.ipAddressWatchers = append(mst.ipAddressWatchers, w)
	return w
}

// nextIPAddressWatcher returns an IP address watcher .
func (mst *mockState) nextIPAddressWatcher() *mockIPAddressWatcher {
	if len(mst.ipAddressWatchers) == 0 {
		panic("ran out of watchers")
	}

	w := mst.ipAddressWatchers[0]
	mst.ipAddressWatchers = mst.ipAddressWatchers[1:]
	if len(w.initial) == 0 {
		ids := make([]string, 0, len(mst.ipAddresses))
		// Initial event - all IP address ids, sorted.
		for id := range mst.ipAddresses {
			ids = append(ids, id)
		}
		sort.Strings(ids)
		w.initial = ids
	}
	w.start()
	return w
}

// removeIPAddress is used by mockIPAddress.Remove()
func (mst *mockState) removeIPAddress(value string) error {
	mst.mu.Lock()
	defer mst.mu.Unlock()

	ipAddr, ok := mst.ipAddresses[value]
	if !ok {
		return jujutxn.ErrNoOperations
	}
	if ipAddr.life != state.Dead {
		return errors.Errorf("cannot remove IP address %q: IP address is not dead", ipAddr.value)
	}
	delete(mst.ipAddresses, value)
	return nil
}

// StartSync implements statetesting.SyncStarter, so mockState can be
// used with watcher helpers/checkers.
func (mst *mockState) StartSync() {}

func (mst *mockState) setUpIPAddresses() {
	ips := []struct {
		value string
		uuid  string
		life  state.Life
	}{
		{"0.1.2.3", "00000000-1111-2222-3333-0123456789ab", state.Alive},
		{"0.1.2.4", "00000000-1111-2222-4444-0123456789ab", state.Alive},
		{"0.1.2.5", "00000000-1111-2222-5555-0123456789ab", state.Alive},
		{"0.1.2.6", "00000000-1111-2222-6666-0123456789ab", state.Dead},
		{"0.1.2.7", "00000000-1111-2222-7777-0123456789ab", state.Dead},
	}
	for _, ip := range ips {
		mst.ipAddresses[ip.value] = &mockIPAddress{
			stub:  mst.stub,
			st:    mst,
			value: ip.value,
			tag:   names.NewIPAddressTag(ip.uuid),
			life:  ip.life,
		}
	}
}

// mockIPAddress implements StateIPAddress for testing.
type mockIPAddress struct {
	addresser.StateIPAddress

	mu   sync.Mutex
	stub *testing.Stub

	st    *mockState
	value string
	tag   names.Tag
	life  state.Life
}

var _ addresser.StateIPAddress = (*mockIPAddress)(nil)

// Tag implements StateIPAddress.
func (mip *mockIPAddress) Tag() names.Tag {
	mip.mu.Lock()
	defer mip.mu.Unlock()

	mip.stub.MethodCall(mip, "Tag")
	mip.stub.NextErr() // Consume the unused error.
	return mip.tag
}

// Life implements StateIPAddress.
func (mip *mockIPAddress) Life() state.Life {
	mip.mu.Lock()
	defer mip.mu.Unlock()

	mip.stub.MethodCall(mip, "Life")
	mip.stub.NextErr() // Consume the unused error.
	return mip.life
}

// EnsureDead implements StateIPAddress.
func (mip *mockIPAddress) EnsureDead() error {
	mip.mu.Lock()
	defer mip.mu.Unlock()

	if err := mip.stub.NextErr(); err != nil {
		return err
	}
	mip.stub.MethodCall(mip, "EnsureDead")
	mip.life = state.Dead
	return nil
}

// Remove implements StateIPAddress.
func (mip *mockIPAddress) Remove() error {
	mip.mu.Lock()
	defer mip.mu.Unlock()

	if err := mip.stub.NextErr(); err != nil {
		return err
	}
	mip.stub.MethodCall(mip, "Remove")
	return mip.st.removeIPAddress(mip.value)
}

// mockBaseWatcher is a helper for the watcher mocks.
type mockBaseWatcher struct {
	err error

	closeChanges func()
	done         chan struct{}
}

var _ state.Watcher = (*mockBaseWatcher)(nil)

func newMockBaseWatcher(err error, closeChanges func()) *mockBaseWatcher {
	w := &mockBaseWatcher{
		err:          err,
		closeChanges: closeChanges,
		done:         make(chan struct{}),
	}
	if err != nil {
		w.Stop()
	}
	return w
}

// Kill implements state.Watcher.
func (mbw *mockBaseWatcher) Kill() {}

// Stop implements state.Watcher.
func (mbw *mockBaseWatcher) Stop() error {
	select {
	case <-mbw.done:
		// Closed.
	default:
		// Signal the loop we want to stop.
		close(mbw.done)
		// Signal the clients we've closed.
		mbw.closeChanges()
	}
	return mbw.err
}

// Wait implements state.Watcher.
func (mbw *mockBaseWatcher) Wait() error {
	return mbw.Stop()
}

// Err implements state.Watcher.
func (mbw *mockBaseWatcher) Err() error {
	return mbw.err
}

// mockConfigWatcher notifies about config changes.
type mockConfigWatcher struct {
	*mockBaseWatcher

	wontStart bool
	incoming  chan struct{}
	changes   chan struct{}
}

var _ state.NotifyWatcher = (*mockConfigWatcher)(nil)

func newMockConfigWatcher(err error) *mockConfigWatcher {
	changes := make(chan struct{})
	mcw := &mockConfigWatcher{
		wontStart:       false,
		incoming:        make(chan struct{}),
		changes:         changes,
		mockBaseWatcher: newMockBaseWatcher(err, func() { close(changes) }),
	}
	return mcw
}

// start starts the backend loop depending on a field setting.
func (mcw *mockConfigWatcher) start() {
	if mcw.wontStart {
		// Set manually by tests that need it.
		mcw.Stop()
		return
	}
	go mcw.loop()
}

func (mcw *mockConfigWatcher) loop() {
	// Prepare initial event.
	outChanges := mcw.changes
	// Forward any incoming changes until stopped.
	for {
		select {
		case <-mcw.done:
			return
		case outChanges <- struct{}{}:
			outChanges = nil
		case <-mcw.incoming:
			outChanges = mcw.changes
		}
	}
}

// Changes implements state.NotifyWatcher.
func (mcw *mockConfigWatcher) Changes() <-chan struct{} {
	return mcw.changes
}

// mockIPAddressWatcher notifies about IP address changes.
type mockIPAddressWatcher struct {
	*mockBaseWatcher

	initial   []string
	wontStart bool
	incoming  chan []string
	changes   chan []string
}

var _ state.StringsWatcher = (*mockIPAddressWatcher)(nil)

func newMockIPAddressWatcher(initial []string) *mockIPAddressWatcher {
	changes := make(chan []string)
	mipw := &mockIPAddressWatcher{
		initial:         initial,
		wontStart:       false,
		changes:         changes,
		incoming:        make(chan []string),
		mockBaseWatcher: newMockBaseWatcher(nil, func() { close(changes) }),
	}
	return mipw
}

// start starts the backend loop depending on a field setting.
func (mipw *mockIPAddressWatcher) start() {
	if mipw.wontStart {
		// Set manually by tests that need it.
		mipw.Stop()
		return
	}
	go mipw.loop()
}

func (mipw *mockIPAddressWatcher) loop() {
	// Prepare initial event.
	unsent := mipw.initial
	outChanges := mipw.changes
	// Forward any incoming changes until stopped.
	for {
		select {
		case <-mipw.done:
			return
		case outChanges <- unsent:
			outChanges = nil
			unsent = nil
		case ids := <-mipw.incoming:
			unsent = append(unsent, ids...)
			outChanges = mipw.changes
		}
	}
}

// Changes implements state.StringsWatcher.
func (mipw *mockIPAddressWatcher) Changes() <-chan []string {
	return mipw.changes
}
<commit_msg>Final changes after review.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package addresser_test

import (
	"sort"
	"sync"

	gc "gopkg.in/check.v1"

	"github.com/juju/errors"
	"github.com/juju/names"
	"github.com/juju/testing"
	jujutxn "github.com/juju/txn"

	"github.com/juju/juju/apiserver/addresser"
	"github.com/juju/juju/environs/config"
	"github.com/juju/juju/state"
)

// mockState implements StateInterface and allows inspection of called
// methods.
type mockState struct {
	mu   sync.Mutex
	stub *testing.Stub

	config      *config.Config
	ipAddresses map[string]*mockIPAddress

	configWatchers    []*mockConfigWatcher
	ipAddressWatchers []*mockIPAddressWatcher
}

func newMockState() *mockState {
	mst := &mockState{
		stub:        &testing.Stub{},
		ipAddresses: make(map[string]*mockIPAddress),
	}
	mst.setUpIPAddresses()
	return mst
}

var _ addresser.StateInterface = (*mockState)(nil)

// EnvironConfig implements StateInterface.
func (mst *mockState) EnvironConfig() (*config.Config, error) {
	mst.mu.Lock()
	defer mst.mu.Unlock()

	mst.stub.MethodCall(mst, "EnvironConfig")

	if err := mst.stub.NextErr(); err != nil {
		return nil, err
	}
	return mst.config, nil
}

// setConfig updates the environ config stored internally. Triggers a
// change event for all created config watchers.
func (mst *mockState) setConfig(c *gc.C, newConfig *config.Config) {
	mst.mu.Lock()
	defer mst.mu.Unlock()

	mst.config = newConfig

	// Notify any watchers for the changes.
	for _, w := range mst.configWatchers {
		w.incoming <- struct{}{}
	}
}

// WatchForEnvironConfigChanges implements StateInterface.
func (mst *mockState) WatchForEnvironConfigChanges() state.NotifyWatcher {
	mst.mu.Lock()
	defer mst.mu.Unlock()

	mst.stub.MethodCall(mst, "WatchForEnvironConfigChanges")
	mst.stub.NextErr()
	return mst.nextEnvironConfigChangesWatcher()
}

// addEnvironConfigChangesWatcher adds an environment config changes watches.
func (mst *mockState) addEnvironConfigChangesWatcher(err error) *mockConfigWatcher {
	w := newMockConfigWatcher(err)
	mst.configWatchers = append(mst.configWatchers, w)
	return w
}

// nextEnvironConfigChangesWatcher returns an environment
// config changes watcher.
func (mst *mockState) nextEnvironConfigChangesWatcher() *mockConfigWatcher {
	if len(mst.configWatchers) == 0 {
		panic("ran out of watchers")
	}

	w := mst.configWatchers[0]
	mst.configWatchers = mst.configWatchers[1:]
	w.start()
	return w
}

// FindEntity implements StateInterface.
func (mst *mockState) FindEntity(tag names.Tag) (state.Entity, error) {
	mst.mu.Lock()
	defer mst.mu.Unlock()

	mst.stub.MethodCall(mst, "FindEntity", tag)

	if err := mst.stub.NextErr(); err != nil {
		return nil, err
	}
	if tag == nil {
		return nil, errors.NotValidf("nil tag is") // +" not valid"
	}
	for _, ipAddress := range mst.ipAddresses {
		if ipAddress.Tag().String() == tag.String() {
			return ipAddress, nil
		}
	}
	return nil, errors.NotFoundf("IP address %s", tag)
}

// IPAddress implements StateInterface.
func (mst *mockState) IPAddress(value string) (addresser.StateIPAddress, error) {
	mst.mu.Lock()
	defer mst.mu.Unlock()

	mst.stub.MethodCall(mst, "IPAddress", value)

	if err := mst.stub.NextErr(); err != nil {
		return nil, err
	}
	ipAddress, found := mst.ipAddresses[value]
	if !found {
		return nil, errors.NotFoundf("IP address %s", value)
	}
	return ipAddress, nil
}

// WatchIPAddresses implements StateInterface.
func (mst *mockState) WatchIPAddresses() state.StringsWatcher {
	mst.mu.Lock()
	defer mst.mu.Unlock()

	mst.stub.MethodCall(mst, "WatchIPAddresses")
	mst.stub.NextErr()
	return mst.nextIPAddressWatcher()
}

// addIPAddressWatcher adds an IP address watcher with the given IDs.
func (mst *mockState) addIPAddressWatcher(ids ...string) *mockIPAddressWatcher {
	w := newMockIPAddressWatcher(ids)
	mst.ipAddressWatchers = append(mst.ipAddressWatchers, w)
	return w
}

// nextIPAddressWatcher returns an IP address watcher .
func (mst *mockState) nextIPAddressWatcher() *mockIPAddressWatcher {
	if len(mst.ipAddressWatchers) == 0 {
		panic("ran out of watchers")
	}

	w := mst.ipAddressWatchers[0]
	mst.ipAddressWatchers = mst.ipAddressWatchers[1:]
	if len(w.initial) == 0 {
		ids := make([]string, 0, len(mst.ipAddresses))
		// Initial event - all IP address ids, sorted.
		for id := range mst.ipAddresses {
			ids = append(ids, id)
		}
		sort.Strings(ids)
		w.initial = ids
	}
	w.start()
	return w
}

// removeIPAddress is used by mockIPAddress.Remove()
func (mst *mockState) removeIPAddress(value string) error {
	mst.mu.Lock()
	defer mst.mu.Unlock()

	ipAddr, ok := mst.ipAddresses[value]
	if !ok {
		return jujutxn.ErrNoOperations
	}
	if ipAddr.life != state.Dead {
		return errors.Errorf("cannot remove IP address %q: IP address is not dead", ipAddr.value)
	}
	delete(mst.ipAddresses, value)
	return nil
}

// StartSync implements statetesting.SyncStarter, so mockState can be
// used with watcher helpers/checkers.
func (mst *mockState) StartSync() {}

func (mst *mockState) setUpIPAddresses() {
	ips := []struct {
		value string
		uuid  string
		life  state.Life
	}{
		{"0.1.2.3", "00000000-1111-2222-3333-0123456789ab", state.Alive},
		{"0.1.2.4", "00000000-1111-2222-4444-0123456789ab", state.Alive},
		{"0.1.2.5", "00000000-1111-2222-5555-0123456789ab", state.Alive},
		{"0.1.2.6", "00000000-1111-2222-6666-0123456789ab", state.Dead},
		{"0.1.2.7", "00000000-1111-2222-7777-0123456789ab", state.Dead},
	}
	for _, ip := range ips {
		mst.ipAddresses[ip.value] = &mockIPAddress{
			stub:  mst.stub,
			st:    mst,
			value: ip.value,
			tag:   names.NewIPAddressTag(ip.uuid),
			life:  ip.life,
		}
	}
}

// mockIPAddress implements StateIPAddress for testing.
type mockIPAddress struct {
	addresser.StateIPAddress

	mu   sync.Mutex
	stub *testing.Stub

	st    *mockState
	value string
	tag   names.Tag
	life  state.Life
}

var _ addresser.StateIPAddress = (*mockIPAddress)(nil)

// Tag implements StateIPAddress.
func (mip *mockIPAddress) Tag() names.Tag {
	mip.mu.Lock()
	defer mip.mu.Unlock()

	mip.stub.MethodCall(mip, "Tag")
	mip.stub.NextErr() // Consume the unused error.
	return mip.tag
}

// Life implements StateIPAddress.
func (mip *mockIPAddress) Life() state.Life {
	mip.mu.Lock()
	defer mip.mu.Unlock()

	mip.stub.MethodCall(mip, "Life")
	mip.stub.NextErr() // Consume the unused error.
	return mip.life
}

// EnsureDead implements StateIPAddress.
func (mip *mockIPAddress) EnsureDead() error {
	mip.mu.Lock()
	defer mip.mu.Unlock()

	mip.stub.MethodCall(mip, "EnsureDead")
	if err := mip.stub.NextErr(); err != nil {
		return err
	}
	mip.life = state.Dead
	return nil
}

// Remove implements StateIPAddress.
func (mip *mockIPAddress) Remove() error {
	mip.mu.Lock()
	defer mip.mu.Unlock()

	mip.stub.MethodCall(mip, "Remove")
	if err := mip.stub.NextErr(); err != nil {
		return err
	}
	return mip.st.removeIPAddress(mip.value)
}

// mockBaseWatcher is a helper for the watcher mocks.
type mockBaseWatcher struct {
	err error

	closeChanges func()
	done         chan struct{}
}

var _ state.Watcher = (*mockBaseWatcher)(nil)

func newMockBaseWatcher(err error, closeChanges func()) *mockBaseWatcher {
	w := &mockBaseWatcher{
		err:          err,
		closeChanges: closeChanges,
		done:         make(chan struct{}),
	}
	if err != nil {
		w.Stop()
	}
	return w
}

// Kill implements state.Watcher.
func (mbw *mockBaseWatcher) Kill() {}

// Stop implements state.Watcher.
func (mbw *mockBaseWatcher) Stop() error {
	select {
	case <-mbw.done:
		// Closed.
	default:
		// Signal the loop we want to stop.
		close(mbw.done)
		// Signal the clients we've closed.
		mbw.closeChanges()
	}
	return mbw.err
}

// Wait implements state.Watcher.
func (mbw *mockBaseWatcher) Wait() error {
	return mbw.Stop()
}

// Err implements state.Watcher.
func (mbw *mockBaseWatcher) Err() error {
	return mbw.err
}

// mockConfigWatcher notifies about config changes.
type mockConfigWatcher struct {
	*mockBaseWatcher

	wontStart bool
	incoming  chan struct{}
	changes   chan struct{}
}

var _ state.NotifyWatcher = (*mockConfigWatcher)(nil)

func newMockConfigWatcher(err error) *mockConfigWatcher {
	changes := make(chan struct{})
	mcw := &mockConfigWatcher{
		wontStart:       false,
		incoming:        make(chan struct{}),
		changes:         changes,
		mockBaseWatcher: newMockBaseWatcher(err, func() { close(changes) }),
	}
	return mcw
}

// start starts the backend loop depending on a field setting.
func (mcw *mockConfigWatcher) start() {
	if mcw.wontStart {
		// Set manually by tests that need it.
		mcw.Stop()
		return
	}
	go mcw.loop()
}

func (mcw *mockConfigWatcher) loop() {
	// Prepare initial event.
	outChanges := mcw.changes
	// Forward any incoming changes until stopped.
	for {
		select {
		case <-mcw.done:
			return
		case outChanges <- struct{}{}:
			outChanges = nil
		case <-mcw.incoming:
			outChanges = mcw.changes
		}
	}
}

// Changes implements state.NotifyWatcher.
func (mcw *mockConfigWatcher) Changes() <-chan struct{} {
	return mcw.changes
}

// mockIPAddressWatcher notifies about IP address changes.
type mockIPAddressWatcher struct {
	*mockBaseWatcher

	initial   []string
	wontStart bool
	incoming  chan []string
	changes   chan []string
}

var _ state.StringsWatcher = (*mockIPAddressWatcher)(nil)

func newMockIPAddressWatcher(initial []string) *mockIPAddressWatcher {
	changes := make(chan []string)
	mipw := &mockIPAddressWatcher{
		initial:         initial,
		wontStart:       false,
		changes:         changes,
		incoming:        make(chan []string),
		mockBaseWatcher: newMockBaseWatcher(nil, func() { close(changes) }),
	}
	return mipw
}

// start starts the backend loop depending on a field setting.
func (mipw *mockIPAddressWatcher) start() {
	if mipw.wontStart {
		// Set manually by tests that need it.
		mipw.Stop()
		return
	}
	go mipw.loop()
}

func (mipw *mockIPAddressWatcher) loop() {
	// Prepare initial event.
	unsent := mipw.initial
	outChanges := mipw.changes
	// Forward any incoming changes until stopped.
	for {
		select {
		case <-mipw.done:
			return
		case outChanges <- unsent:
			outChanges = nil
			unsent = nil
		case ids := <-mipw.incoming:
			unsent = append(unsent, ids...)
			outChanges = mipw.changes
		}
	}
}

// Changes implements state.StringsWatcher.
func (mipw *mockIPAddressWatcher) Changes() <-chan []string {
	return mipw.changes
}
<|endoftext|>


<commit_before>package service

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"

	"go.opencensus.io/stats"
	"go.opencensus.io/stats/view"
	"go.opencensus.io/tag"

	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/log"
	"github.com/ovh/cds/sdk/telemetry"
)

type NamedService interface {
	Name() string
	Type() string
}

// GetPrometheustMetricsHandler returns a Handler to exposer prometheus views
func GetPrometheustMetricsHandler(s NamedService) func() Handler {
	return func() Handler {
		return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
			if telemetry.StatsExporter(ctx) == nil {
				return sdk.WithStack(sdk.ErrNotFound)
			}
			telemetry.StatsExporter(ctx).ServeHTTP(w, r)
			return nil
		}
	}
}

func GetMetricsHandler() Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		var e = telemetry.StatsExporter(ctx)
		if e == nil {
			return sdk.WithStack(sdk.ErrNotFound)
		}
		return WriteJSON(w, e.ExposedViews, http.StatusOK)
	}
}

func (c *Common) CommonMetricsHandler() Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {

		var bToMb = func(b uint64) uint64 {
			return b / 1024 / 1024
		}

		var m runtime.MemStats
		// https://golang.org/pkg/runtime/#MemStats
		runtime.ReadMemStats(&m)

		// Alloc is bytes of allocated heap objects.
		// "Allocated" heap objects include all reachable objects, as
		// well as unreachable objects that the garbage collector has
		// not yet freed. Specifically, HeapAlloc increases as heap
		// objects are allocated and decreases as the heap is swept
		// and unreachable objects are freed. Sweeping occurs
		// incrementally between GC cycles, so these two processes
		// occur simultaneously, and as a result HeapAlloc tends to
		// change smoothly (in contrast with the sawtooth that is
		// typical of stop-the-world garbage collectors).

		// TotalAlloc is cumulative bytes allocated for heap objects.
		// TotalAlloc increases as heap objects are allocated, but
		// unlike Alloc and HeapAlloc, it does not decrease when
		// objects are freed.

		// Sys is the total bytes of memory obtained from the OS.
		// Sys is the sum of the XSys fields below. Sys measures the
		// virtual address space reserved by the Go runtime for the
		// heap, stacks, and other internal data structures. It's
		// likely that not all of the virtual address space is backed
		// by physical memory at any given moment, though in general
		// it all was at some point.

		// NumGC is the number of completed GC cycles.
		return WriteJSON(w, map[string]uint64{
			"alloc":       bToMb(m.Alloc),
			"total_alloc": bToMb(m.TotalAlloc),
			"sys":         bToMb(m.Sys),
			"gc":          uint64(m.NumGC),
		}, http.StatusOK)

	}
}

var onceMetrics sync.Once

func (c *Common) RegisterCommonMetricsView(ctx context.Context) {
	onceMetrics.Do(func() {
		allocStats := stats.Int64(
			"cds/alloc",
			"Alloc is bytes of allocated heap objects",
			stats.UnitBytes,
		)

		totalAllocStats := stats.Int64(
			fmt.Sprintf("cds/total_alloc"),
			"Total Alloc is cumulative bytes allocated for heap objects",
			stats.UnitBytes,
		)

		sysStats := stats.Int64(
			fmt.Sprintf("cds/sys"),
			"Sys is the total bytes of memory obtained from the OS",
			stats.UnitBytes,
		)

		gcStats := stats.Int64(
			fmt.Sprintf("cds/num_gc"),
			"NumGC is the number of completed GC cycles",
			stats.UnitDimensionless,
		)

		tagServiceType := telemetry.MustNewKey(telemetry.TagServiceType)
		tagServiceName := telemetry.MustNewKey(telemetry.TagServiceName)
		tagHostname := telemetry.MustNewKey(telemetry.TagHostname)

		allocView := view.View{
			Name:        "cds/mem/alloc",
			Description: allocStats.Description(),
			Measure:     allocStats,
			TagKeys:     []tag.Key{tagServiceType, tagServiceName, tagHostname},
			Aggregation: view.LastValue(),
		}

		totalAllocView := view.View{
			Name:        "cds/mem/total_alloc",
			Description: totalAllocStats.Description(),
			Measure:     totalAllocStats,
			TagKeys:     []tag.Key{tagServiceType, tagServiceName, tagHostname},
			Aggregation: view.LastValue(),
		}

		sysView := view.View{
			Name:        "cds/mem/sys",
			Description: sysStats.Description(),
			Measure:     sysStats,
			TagKeys:     []tag.Key{tagServiceType, tagServiceName, tagHostname},
			Aggregation: view.LastValue(),
		}

		gcView := view.View{
			Name:        "cds/mem/gc",
			Description: gcStats.Description(),
			Measure:     gcStats,
			TagKeys:     []tag.Key{tagServiceType, tagServiceName, tagHostname},
			Aggregation: view.LastValue(),
		}

		if err := telemetry.RegisterView(ctx, &allocView, &totalAllocView, &sysView, &gcView); err != nil {
			// This should not append
			panic(fmt.Errorf("unable to register service metrics view: %v", err))
		}

		c.GoRoutines.Run(ctx, "service_metrics", func(ctx context.Context) {
			var maxMemoryS = os.Getenv("CDS_MAX_HEAP_SIZE") // in bytes
			var maxMemory uint64
			var onceMaxMemorySignal = new(sync.Once)
			if maxMemoryS != "" {
				maxMemory, _ = strconv.ParseUint(maxMemoryS, 10, 64)
			}
			hostname, _ := os.Hostname()
			ctx = telemetry.ContextWithTag(ctx, tagHostname, hostname)

			var tick = time.NewTicker(10 * time.Second)
			defer tick.Stop()
			for {
				select {
				case <-ctx.Done():
					return
				case <-tick.C:
					var m runtime.MemStats
					runtime.ReadMemStats(&m)
					stats.Record(ctx, allocStats.M(int64(m.Alloc)))
					stats.Record(ctx, totalAllocStats.M(int64(m.TotalAlloc)))
					stats.Record(ctx, sysStats.M(int64(m.Sys)))
					stats.Record(ctx, gcStats.M(int64(m.NumGC)))

					if maxMemory > 0 && m.Alloc >= maxMemory {
						onceMaxMemorySignal.Do(func() {
							p, err := os.FindProcess(os.Getpid())
							if err != nil {
								log.Error(ctx, "unable to find current process: %v", err)
								return
							}
							if err := p.Signal(sdk.SIGINFO); err != nil {
								log.Error(ctx, "unable to send signal: %v", err)
								return
							}
							log.Info(ctx, "metrics> SIGINFO signal send to %v", os.Getpid())
						})
					}
				}
			}
		})
	})
}

func writeJSON(w http.ResponseWriter, i interface{}, statusCode int) error {
	btes, _ := json.Marshal(i)
	w.Header().Add("Content-Type", "application/json")
	w.Header().Add("Content-Length", fmt.Sprintf("%d", len(btes)))
	w.WriteHeader(statusCode)
	_, err := w.Write(btes)
	return sdk.WithStack(err)
}

func GetMetricHandler(prefix string) func() Handler {
	return func() Handler {
		return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
			exporter := telemetry.StatsExporter(ctx)
			if exporter == nil {
				return sdk.WithStack(sdk.ErrNotFound)
			}
			view := strings.TrimPrefix(r.URL.Path, prefix)
			formValues := r.URL.Query()
			tags := make(map[string]string)
			threshold := formValues.Get("threshold")
			for k := range formValues {
				if k != "threshold" {
					tags[k] = formValues.Get(k)
				}
			}
			log.Debug("GetMetricHandler> path: %s - tags: %v", view, tags)

			if view == "" {
				return writeJSON(w, exporter, http.StatusOK)
			}

			metricsView := exporter.GetView(view, tags)
			if metricsView == nil {
				return sdk.WithStack(sdk.ErrNotFound)
			}

			statusCode := http.StatusOK
			if threshold != "" {
				thresholdF, err := strconv.ParseFloat(threshold, 64)
				if err != nil {
					return sdk.WithStack(sdk.ErrWrongRequest)
				}
				if metricsView.Value >= thresholdF {
					log.Error(context.Background(), "GetMetricHandler> %s threshold (%s) reached or exceeded : %v", metricsView.Name, threshold, metricsView.Value)
					statusCode = 509 // Bandwidth Limit Exceeded
				}
			}

			return writeJSON(w, metricsView, statusCode)
		}
	}
}
<commit_msg>fix(engine): service metrics hostname tag<commit_after>package service

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"

	"go.opencensus.io/stats"
	"go.opencensus.io/stats/view"
	"go.opencensus.io/tag"

	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/log"
	"github.com/ovh/cds/sdk/telemetry"
)

type NamedService interface {
	Name() string
	Type() string
}

// GetPrometheustMetricsHandler returns a Handler to exposer prometheus views
func GetPrometheustMetricsHandler(s NamedService) func() Handler {
	return func() Handler {
		return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
			if telemetry.StatsExporter(ctx) == nil {
				return sdk.WithStack(sdk.ErrNotFound)
			}
			telemetry.StatsExporter(ctx).ServeHTTP(w, r)
			return nil
		}
	}
}

func GetMetricsHandler() Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		var e = telemetry.StatsExporter(ctx)
		if e == nil {
			return sdk.WithStack(sdk.ErrNotFound)
		}
		return WriteJSON(w, e.ExposedViews, http.StatusOK)
	}
}

func (c *Common) CommonMetricsHandler() Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {

		var bToMb = func(b uint64) uint64 {
			return b / 1024 / 1024
		}

		var m runtime.MemStats
		// https://golang.org/pkg/runtime/#MemStats
		runtime.ReadMemStats(&m)

		// Alloc is bytes of allocated heap objects.
		// "Allocated" heap objects include all reachable objects, as
		// well as unreachable objects that the garbage collector has
		// not yet freed. Specifically, HeapAlloc increases as heap
		// objects are allocated and decreases as the heap is swept
		// and unreachable objects are freed. Sweeping occurs
		// incrementally between GC cycles, so these two processes
		// occur simultaneously, and as a result HeapAlloc tends to
		// change smoothly (in contrast with the sawtooth that is
		// typical of stop-the-world garbage collectors).

		// TotalAlloc is cumulative bytes allocated for heap objects.
		// TotalAlloc increases as heap objects are allocated, but
		// unlike Alloc and HeapAlloc, it does not decrease when
		// objects are freed.

		// Sys is the total bytes of memory obtained from the OS.
		// Sys is the sum of the XSys fields below. Sys measures the
		// virtual address space reserved by the Go runtime for the
		// heap, stacks, and other internal data structures. It's
		// likely that not all of the virtual address space is backed
		// by physical memory at any given moment, though in general
		// it all was at some point.

		// NumGC is the number of completed GC cycles.
		return WriteJSON(w, map[string]uint64{
			"alloc":       bToMb(m.Alloc),
			"total_alloc": bToMb(m.TotalAlloc),
			"sys":         bToMb(m.Sys),
			"gc":          uint64(m.NumGC),
		}, http.StatusOK)

	}
}

var onceMetrics sync.Once

func (c *Common) RegisterCommonMetricsView(ctx context.Context) {
	onceMetrics.Do(func() {
		allocStats := stats.Int64(
			"cds/alloc",
			"Alloc is bytes of allocated heap objects",
			stats.UnitBytes,
		)

		totalAllocStats := stats.Int64(
			fmt.Sprintf("cds/total_alloc"),
			"Total Alloc is cumulative bytes allocated for heap objects",
			stats.UnitBytes,
		)

		sysStats := stats.Int64(
			fmt.Sprintf("cds/sys"),
			"Sys is the total bytes of memory obtained from the OS",
			stats.UnitBytes,
		)

		gcStats := stats.Int64(
			fmt.Sprintf("cds/num_gc"),
			"NumGC is the number of completed GC cycles",
			stats.UnitDimensionless,
		)

		tagServiceType := telemetry.MustNewKey(telemetry.TagServiceType)
		tagServiceName := telemetry.MustNewKey(telemetry.TagServiceName)
		tagHostname := telemetry.MustNewKey(telemetry.TagHostname)

		allocView := view.View{
			Name:        "cds/mem/alloc",
			Description: allocStats.Description(),
			Measure:     allocStats,
			TagKeys:     []tag.Key{tagServiceType, tagServiceName, tagHostname},
			Aggregation: view.LastValue(),
		}

		totalAllocView := view.View{
			Name:        "cds/mem/total_alloc",
			Description: totalAllocStats.Description(),
			Measure:     totalAllocStats,
			TagKeys:     []tag.Key{tagServiceType, tagServiceName, tagHostname},
			Aggregation: view.LastValue(),
		}

		sysView := view.View{
			Name:        "cds/mem/sys",
			Description: sysStats.Description(),
			Measure:     sysStats,
			TagKeys:     []tag.Key{tagServiceType, tagServiceName, tagHostname},
			Aggregation: view.LastValue(),
		}

		gcView := view.View{
			Name:        "cds/mem/gc",
			Description: gcStats.Description(),
			Measure:     gcStats,
			TagKeys:     []tag.Key{tagServiceType, tagServiceName, tagHostname},
			Aggregation: view.LastValue(),
		}

		if err := telemetry.RegisterView(ctx, &allocView, &totalAllocView, &sysView, &gcView); err != nil {
			// This should not append
			panic(fmt.Errorf("unable to register service metrics view: %v", err))
		}

		c.GoRoutines.Run(ctx, "service_metrics", func(ctx context.Context) {
			var maxMemoryS = os.Getenv("CDS_MAX_HEAP_SIZE") // in bytes
			var maxMemory uint64
			var onceMaxMemorySignal = new(sync.Once)
			if maxMemoryS != "" {
				maxMemory, _ = strconv.ParseUint(maxMemoryS, 10, 64)
			}
			hostname, _ := os.Hostname()
			ctx = telemetry.ContextWithTag(ctx, telemetry.TagHostname, hostname)

			var tick = time.NewTicker(10 * time.Second)
			defer tick.Stop()
			for {
				select {
				case <-ctx.Done():
					return
				case <-tick.C:
					var m runtime.MemStats
					runtime.ReadMemStats(&m)
					stats.Record(ctx, allocStats.M(int64(m.Alloc)))
					stats.Record(ctx, totalAllocStats.M(int64(m.TotalAlloc)))
					stats.Record(ctx, sysStats.M(int64(m.Sys)))
					stats.Record(ctx, gcStats.M(int64(m.NumGC)))

					if maxMemory > 0 && m.Alloc >= maxMemory {
						onceMaxMemorySignal.Do(func() {
							p, err := os.FindProcess(os.Getpid())
							if err != nil {
								log.Error(ctx, "unable to find current process: %v", err)
								return
							}
							if err := p.Signal(sdk.SIGINFO); err != nil {
								log.Error(ctx, "unable to send signal: %v", err)
								return
							}
							log.Info(ctx, "metrics> SIGINFO signal send to %v", os.Getpid())
						})
					}
				}
			}
		})
	})
}

func writeJSON(w http.ResponseWriter, i interface{}, statusCode int) error {
	btes, _ := json.Marshal(i)
	w.Header().Add("Content-Type", "application/json")
	w.Header().Add("Content-Length", fmt.Sprintf("%d", len(btes)))
	w.WriteHeader(statusCode)
	_, err := w.Write(btes)
	return sdk.WithStack(err)
}

func GetMetricHandler(prefix string) func() Handler {
	return func() Handler {
		return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
			exporter := telemetry.StatsExporter(ctx)
			if exporter == nil {
				return sdk.WithStack(sdk.ErrNotFound)
			}
			view := strings.TrimPrefix(r.URL.Path, prefix)
			formValues := r.URL.Query()
			tags := make(map[string]string)
			threshold := formValues.Get("threshold")
			for k := range formValues {
				if k != "threshold" {
					tags[k] = formValues.Get(k)
				}
			}
			log.Debug("GetMetricHandler> path: %s - tags: %v", view, tags)

			if view == "" {
				return writeJSON(w, exporter, http.StatusOK)
			}

			metricsView := exporter.GetView(view, tags)
			if metricsView == nil {
				return sdk.WithStack(sdk.ErrNotFound)
			}

			statusCode := http.StatusOK
			if threshold != "" {
				thresholdF, err := strconv.ParseFloat(threshold, 64)
				if err != nil {
					return sdk.WithStack(sdk.ErrWrongRequest)
				}
				if metricsView.Value >= thresholdF {
					log.Error(context.Background(), "GetMetricHandler> %s threshold (%s) reached or exceeded : %v", metricsView.Name, threshold, metricsView.Value)
					statusCode = 509 // Bandwidth Limit Exceeded
				}
			}

			return writeJSON(w, metricsView, statusCode)
		}
	}
}
<|endoftext|>


<commit_before>/*
Package midi allows you to listen to incoming MIDI messages.

Warning!  This is not a feature-complete MIDI implementation.  MIDI system messages are ignored with the exception of CLOCK, START, and STOP messages.

Example

If you already have a main event loop, do this:

 // Launch the MIDI reader thread and get the channel it uses to deliver incoming messages
 midiMessageChan := midi.GetMidiMessageStream("/dev/midi1")

 // Create a persistent object which knows the current MIDI state
 // This includes the current volumes of all notes (will be 0 for non-playing notes)
 // and the current value of all controllers.
 midiState := midi.MidiState{}

 // Your main event loop
 for {
     // Nonblockingly get all the MIDI messages that have come in since we last checked
     // and use them to update the MIDI state
     midiState.UpdateStateFromChannel(midiMessageChan)

     // Do things with the MIDI data
     if midiState.KeyVolumes[60] > 0 {
         fmt.Println("Key 60 is held down right now")
     }
 }

*/
package midi

import (
	"fmt"
	"os"
	"time"
)

//================================================================================
// CONSTANTS

const RETRY_WAIT = 2 // seconds to wait before retrying opening midi device file

// kinds of messages

const NOTE_OFF = byte(0x80)
const NOTE_ON = byte(0x90)
const AFTERTOUCH = byte(0xa0)
const CONTROLLER = byte(0xb0)
const PROGRAM_CHANGE = byte(0xc0)
const CHANNEL_PRESSURE = byte(0xd0)
const PITCH_BEND = byte(0xe0)
const SYSTEM = byte(0xf0)

// special channel numbers for SYSTEM messages

const CLOCK = byte(8)
const START = byte(10)
const STOP = byte(12)

//================================================================================
// MIDIMESSAGE TYPE

type MidiMessage struct {
	Kind    byte // one of the constants above
	Channel byte // either a channel number of one of the special channel constants CLOCK, START, STOP
	Key     byte // key, controller, or instrument
	Value   byte // velocity, touch, controller value, or channel pressure
}

func debug(s string) {
	//fmt.Println("    [midi]", s)
}

// Convert the MidiMessage to a human-readable string so it can be printed
func (m *MidiMessage) String() string {
	kindStr := "other"
	switch m.Kind {
	case NOTE_OFF:
		kindStr = "NOTE_OFF"
	case NOTE_ON:
		kindStr = "NOTE_ON"
	case AFTERTOUCH:
		kindStr = "AFTERTOUCH"
	case CONTROLLER:
		kindStr = "CONTROLLER"
	case SYSTEM:
		kindStr = "SYSTEM"
	}
	if kindStr == "other" {
		return fmt.Sprintf("(%#x ch=%v key=%v val=%v)", m.Kind, m.Channel, m.Key, m.Value)
	} else {
		return fmt.Sprintf("(%s ch=%v key=%v val=%v)", kindStr, m.Channel, m.Key, m.Value)
	}
}

//================================================================================
// PARSE MIDI BYTES INTO MESSAGE OBJECTS

// Read a stream of raw MIDI bytes on inCh, parse them into *MidiMessage structs,
// and send over outCh.
// It accepts CLOCK, START, and STOP system messages but other system messages
// are ignored.
func MidiStreamParserThread(inCh chan byte, outCh chan *MidiMessage) {
	debug("starting thread")
	message := new(MidiMessage) // pointer
	ii := 0
	for b := range inCh {
		debug("")
		debug(fmt.Sprintf("ii = %v, got byte %v", ii, b))
		debug(fmt.Sprintf("current message = %v", message))

		// special handling for control bytes which mark the start of messages
		if b >= 128 {
			debug("     this is a control byte")
			message = new(MidiMessage)
			message.Kind = b & 0xf0
			message.Channel = b & 0x0f
			ii = 1
			if message.Kind == SYSTEM && (message.Channel == CLOCK || message.Channel == START || message.Channel == STOP) {
				debug("sending")
				outCh <- message
				ii = 0
			}
			continue
		}

		// handle data bytes differently depending on what number they are in a message
		debug("     this is a data byte")
		if ii == 0 {
			// we should never get a data byte at ii = 0.
			// if it happens it means we're not understanding this part of the stream,
			// so drop the byte and do nothing until we get another control byte.
			continue
		} else if ii == 1 {
			debug("     1")
			message.Key = b
			// these kinds expect 1 data byte, so we might be done:
			if message.Kind == PROGRAM_CHANGE || message.Kind == CHANNEL_PRESSURE {
				debug("sending")
				outCh <- message
				ii = 0
				continue
			}
		} else if ii == 2 {
			debug("     2")
			message.Value = b
			// these kinds expect 2 data bytes, so we might be done:
			if message.Kind == NOTE_OFF || message.Kind == NOTE_ON || message.Kind == AFTERTOUCH || message.Kind == CONTROLLER || message.Kind == PITCH_BEND {
				debug("sending")
				outCh <- message
				ii = 0
				continue
			}
		} else {
			// we're ignoring messages with more than 2 data bytes.
			// do nothing, wait for another control byte
		}

		ii += 1
	}

	// if we get here, inCh has been closed
	debug(" thread is done")
	close(outCh)
}

//================================================================================
// HELPERS

// Start some threads which will read and parse incoming MIDI messages in the background.
// Return a channel which emits pointers to MidiMessage structs.
// "path" should be the path to the midi device, e.g. "/dev/midi1".
// If the path can't be opened, it will keep retrying once a second forever until it succeeds.
func GetMidiMessageStream(path string) chan *MidiMessage {
	midiByteChan := make(chan byte, 3000)
	midiMessageChan := make(chan *MidiMessage, 500)

	go tenaciousFileByteStreamerThread(path, midiByteChan)
	go MidiStreamParserThread(midiByteChan, midiMessageChan)

	return midiMessageChan
}

// Stream the bytes from the given path, one byte at a time
// Assumes the file is a special device file which will never hit EOF.
// If the file can't be opened, it will keep trying once a second forver until it succeeds.
func tenaciousFileByteStreamerThread(path string, outCh chan byte) {
	for {
		file, err := os.Open(path)
		if err != nil {
			time.Sleep(time.Duration(RETRY_WAIT * time.Second))
			fmt.Println("[midi] couldn't open midi device:", path, " ... waiting and trying again")
			continue
		}
		defer file.Close()
		fmt.Println("[midi] successfully opened midi device", path)

		buf := make([]byte, 1)
		for {
			count, err := file.Read(buf)
			if err != nil {
				fmt.Println("[midi] couldn't read from midi device:", path)
				fmt.Println(err)
				break
			}
			if count != 1 {
				panic(fmt.Sprintf("[midi] ERROR: when reading, read %s bytes instead of 1", count))
			}
			outCh <- buf[0]
		}
	}
}

//================================================================================
// MIDISTATE TYPE

// Keeps track of the current state of the keys and controllers.
type MidiState struct {
	KeyVolumes         [128]byte      // values from 0 to 127
	ControllerValues   [128]byte      // values from 0 to 127
	RecentMidiMessages []*MidiMessage // midi messages from the most recent call to UpdateStateXXX()
}

// Pull all the available MidiMessages out of the channel without blocking.  Requires a channel
// with a buffer length greater than zero.
// This can deadlock if used by more than one goroutine at a time pulling on the same channel.
func GetAvailableMidiMessages(midiMessageChan chan *MidiMessage) []*MidiMessage {
	result := make([]*MidiMessage, 0)
	for {
		if len(midiMessageChan) == 0 {
			break
		}
		result = append(result, <-midiMessageChan)
	}
	return result
}

// Given a channel of MidiMessages, read all the messages that are available right now
// and update the MidiState object.
// Requires a channel that has a buffer length greater than zero.
func (midiState *MidiState) UpdateStateFromChannel(midiMessageChan chan *MidiMessage) {
	midiState.UpdateStateFromSlice(GetAvailableMidiMessages(midiMessageChan))
}

// Given a slice of MidiMessages, update the MidiState object.
// Note that the MidiState object will keep a pointer to the midiMessages
// object you provide here (as MidiState.RecentMidiMessages).
func (midiState *MidiState) UpdateStateFromSlice(midiMessages []*MidiMessage) {
	midiState.RecentMidiMessages = midiMessages
	for _, m := range midiState.RecentMidiMessages {
		switch m.Kind {
		case NOTE_OFF:
			midiState.KeyVolumes[m.Key] = 0
		case NOTE_ON:
			midiState.KeyVolumes[m.Key] = m.Value
		case CONTROLLER:
			midiState.ControllerValues[m.Key] = m.Value
		}
	}
}
<commit_msg>More MIDI documentation<commit_after>/*
Package midi allows you to listen to incoming MIDI messages.

Warning!  This is not a feature-complete MIDI implementation.  MIDI system messages are ignored with the exception of CLOCK, START, and STOP messages.

Example

If you already have a main event loop, do this:

 // Launch the MIDI reader thread and get the channel it uses to deliver incoming messages
 midiMessageChan := midi.GetMidiMessageStream("/dev/midi1")

 // Create a persistent object which knows the current MIDI state
 // This includes the current volumes of all notes (will be 0 for non-playing notes)
 // and the current value of all controllers.
 midiState := midi.MidiState{}

 // Your main event loop
 for {
     // Nonblockingly get all the MIDI messages that have come in since we last checked
     // and use them to update the MIDI state
     midiState.UpdateStateFromChannel(midiMessageChan)

     // Do things with the MIDI data
     if midiState.KeyVolumes[60] > 0 {
         fmt.Println("Key 60 is held down right now")
     }
 }

For more details on MIDI:

http://www.midi.org/techspecs/midimessages.php

https://en.wikipedia.org/wiki/MIDI_timecode#Quarter-frame_messages

*/
package midi

import (
	"fmt"
	"os"
	"time"
)

//================================================================================
// CONSTANTS

const RETRY_WAIT = 2 // seconds to wait before retrying opening midi device file

// kinds of messages

const NOTE_OFF = byte(0x80)
const NOTE_ON = byte(0x90)
const AFTERTOUCH = byte(0xa0)
const CONTROLLER = byte(0xb0)
const PROGRAM_CHANGE = byte(0xc0)
const CHANNEL_PRESSURE = byte(0xd0)
const PITCH_BEND = byte(0xe0)
const SYSTEM = byte(0xf0)

// special channel numbers for SYSTEM messages

const CLOCK = byte(8)
const START = byte(10)
const STOP = byte(12)

//================================================================================
// MIDIMESSAGE TYPE

type MidiMessage struct {
	Kind    byte // one of the constants above
	Channel byte // either a channel number of one of the special channel constants CLOCK, START, STOP
	Key     byte // key, controller, instrument, or pitch bend lsb
	Value   byte // velocity, touch, controller value, channel pressure, or pitch bend msb
}

func debug(s string) {
	//fmt.Println("    [midi]", s)
}

// Convert the MidiMessage to a human-readable string so it can be printed
func (m *MidiMessage) String() string {
	kindStr := "other"
	switch m.Kind {
	case NOTE_OFF:
		kindStr = "NOTE_OFF"
	case NOTE_ON:
		kindStr = "NOTE_ON"
	case AFTERTOUCH:
		kindStr = "AFTERTOUCH"
	case CONTROLLER:
		kindStr = "CONTROLLER"
	case SYSTEM:
		kindStr = "SYSTEM"
	}
	if kindStr == "other" {
		return fmt.Sprintf("(%#x ch=%v key=%v val=%v)", m.Kind, m.Channel, m.Key, m.Value)
	} else {
		return fmt.Sprintf("(%s ch=%v key=%v val=%v)", kindStr, m.Channel, m.Key, m.Value)
	}
}

//================================================================================
// PARSE MIDI BYTES INTO MESSAGE OBJECTS

// Read a stream of raw MIDI bytes on inCh, parse them into *MidiMessage structs,
// and send over outCh.
// It accepts CLOCK, START, and STOP system messages but other system messages
// are ignored.
func MidiStreamParserThread(inCh chan byte, outCh chan *MidiMessage) {
	debug("starting thread")
	message := new(MidiMessage) // pointer
	ii := 0
	for b := range inCh {
		debug("")
		debug(fmt.Sprintf("ii = %v, got byte %v", ii, b))
		debug(fmt.Sprintf("current message = %v", message))

		// special handling for control bytes which mark the start of messages
		if b >= 128 {
			debug("     this is a control byte")
			message = new(MidiMessage)
			message.Kind = b & 0xf0
			message.Channel = b & 0x0f
			ii = 1
			if message.Kind == SYSTEM && (message.Channel == CLOCK || message.Channel == START || message.Channel == STOP) {
				debug("sending")
				outCh <- message
				ii = 0
			}
			continue
		}

		// handle data bytes differently depending on what number they are in a message
		debug("     this is a data byte")
		if ii == 0 {
			// we should never get a data byte at ii = 0.
			// if it happens it means we're not understanding this part of the stream,
			// so drop the byte and do nothing until we get another control byte.
			continue
		} else if ii == 1 {
			debug("     1")
			message.Key = b
			// these kinds expect 1 data byte, so we might be done:
			if message.Kind == PROGRAM_CHANGE || message.Kind == CHANNEL_PRESSURE {
				debug("sending")
				outCh <- message
				ii = 0
				continue
			}
		} else if ii == 2 {
			debug("     2")
			message.Value = b
			// these kinds expect 2 data bytes, so we might be done:
			if message.Kind == NOTE_OFF || message.Kind == NOTE_ON || message.Kind == AFTERTOUCH || message.Kind == CONTROLLER || message.Kind == PITCH_BEND {
				debug("sending")
				outCh <- message
				ii = 0
				continue
			}
		} else {
			// we're ignoring messages with more than 2 data bytes.
			// do nothing, wait for another control byte
		}

		ii += 1
	}

	// if we get here, inCh has been closed
	debug(" thread is done")
	close(outCh)
}

//================================================================================
// HELPERS

// Start some threads which will read and parse incoming MIDI messages in the background.
// Return a channel which emits pointers to MidiMessage structs.
// "path" should be the path to the midi device, e.g. "/dev/midi1".
// If the path can't be opened, it will keep retrying once a second forever until it succeeds.
func GetMidiMessageStream(path string) chan *MidiMessage {
	midiByteChan := make(chan byte, 3000)
	midiMessageChan := make(chan *MidiMessage, 500)

	go tenaciousFileByteStreamerThread(path, midiByteChan)
	go MidiStreamParserThread(midiByteChan, midiMessageChan)

	return midiMessageChan
}

// Stream the bytes from the given path, one byte at a time
// Assumes the file is a special device file which will never hit EOF.
// If the file can't be opened, it will keep trying once a second forver until it succeeds.
func tenaciousFileByteStreamerThread(path string, outCh chan byte) {
	for {
		file, err := os.Open(path)
		if err != nil {
			time.Sleep(time.Duration(RETRY_WAIT * time.Second))
			fmt.Println("[midi] couldn't open midi device:", path, " ... waiting and trying again")
			continue
		}
		defer file.Close()
		fmt.Println("[midi] successfully opened midi device", path)

		buf := make([]byte, 1)
		for {
			count, err := file.Read(buf)
			if err != nil {
				fmt.Println("[midi] couldn't read from midi device:", path)
				fmt.Println(err)
				break
			}
			if count != 1 {
				panic(fmt.Sprintf("[midi] ERROR: when reading, read %s bytes instead of 1", count))
			}
			outCh <- buf[0]
		}
	}
}

//================================================================================
// MIDISTATE TYPE

// Keeps track of the current state of the keys and controllers.
type MidiState struct {
	KeyVolumes         [128]byte      // values from 0 to 127
	ControllerValues   [128]byte      // values from 0 to 127
	RecentMidiMessages []*MidiMessage // midi messages from the most recent call to UpdateStateXXX()
}

// Pull all the available MidiMessages out of the channel without blocking.  Requires a channel
// with a buffer length greater than zero.
// This can deadlock if used by more than one goroutine at a time pulling on the same channel.
func GetAvailableMidiMessages(midiMessageChan chan *MidiMessage) []*MidiMessage {
	result := make([]*MidiMessage, 0)
	for {
		if len(midiMessageChan) == 0 {
			break
		}
		result = append(result, <-midiMessageChan)
	}
	return result
}

// Given a channel of MidiMessages, read all the messages that are available right now
// and update the MidiState object.
// Requires a channel that has a buffer length greater than zero.
func (midiState *MidiState) UpdateStateFromChannel(midiMessageChan chan *MidiMessage) {
	midiState.UpdateStateFromSlice(GetAvailableMidiMessages(midiMessageChan))
}

// Given a slice of MidiMessages, update the MidiState object.
// Note that the MidiState object will keep a pointer to the midiMessages
// object you provide here (as MidiState.RecentMidiMessages).
func (midiState *MidiState) UpdateStateFromSlice(midiMessages []*MidiMessage) {
	midiState.RecentMidiMessages = midiMessages
	for _, m := range midiState.RecentMidiMessages {
		switch m.Kind {
		case NOTE_OFF:
			midiState.KeyVolumes[m.Key] = 0
		case NOTE_ON:
			midiState.KeyVolumes[m.Key] = m.Value
		case CONTROLLER:
			midiState.ControllerValues[m.Key] = m.Value
		}
	}
}
<|endoftext|>


<commit_before>/*
Package midi allows you to listen to incoming MIDI messages.

Warning!  This is not a feature-complete MIDI implementation.  MIDI system messages are ignored with the exception of CLOCK, START, and STOP messages.

Example

If you already have a main event loop, do this:

 // Launch the MIDI reader thread and get the channel it uses to deliver incoming messages
 midiMessageChan := midi.GetMidiMessageStream("/dev/midi1")

 // Create a persistent object which knows the current MIDI state
 // This includes the current volumes of all notes (will be 0 for non-playing notes)
 // and the current value of all controllers.
 midiState := midi.MidiState{}

 // Your main event loop
 for {
     // Nonblockingly get all the MIDI messages that have come in since we last checked
     // and use them to update the MIDI state
     midiState.UpdateStateFromChannel(midiMessageChan)

     // Do things with the MIDI data
     if midiState.KeyVolumes[60] > 0 {
         fmt.Println("Key 60 is held down right now")
     }
 }

For more details on MIDI:

http://www.midi.org/techspecs/midimessages.php

https://en.wikipedia.org/wiki/MIDI_timecode#Quarter-frame_messages

*/
package midi

import (
	"fmt"
	"os"
	"time"
)

//================================================================================
// CONSTANTS

const RETRY_WAIT = 2 // seconds to wait before retrying opening midi device file

// kinds of messages

const NOTE_OFF = byte(0x80)
const NOTE_ON = byte(0x90)
const AFTERTOUCH = byte(0xa0)
const CONTROLLER = byte(0xb0)
const PROGRAM_CHANGE = byte(0xc0)
const CHANNEL_PRESSURE = byte(0xd0)
const PITCH_BEND = byte(0xe0)
const SYSTEM = byte(0xf0)

// special channel numbers for SYSTEM messages

const CLOCK = byte(8)
const START = byte(10)
const STOP = byte(12)

//================================================================================
// MIDIMESSAGE TYPE

type MidiMessage struct {
	Kind    byte // one of the constants above
	Channel byte // either a channel number of one of the special channel constants CLOCK, START, STOP
	Key     byte // key, controller, instrument, or pitch bend lsb
	Value   byte // velocity, touch, controller value, channel pressure, or pitch bend msb
}

func debug(s string) {
	//fmt.Println("    [midi]", s)
}

// Convert the MidiMessage to a human-readable string so it can be printed
func (m *MidiMessage) String() string {
	kindStr := "other"
	switch m.Kind {
	case NOTE_OFF:
		kindStr = "NOTE_OFF"
	case NOTE_ON:
		kindStr = "NOTE_ON"
	case AFTERTOUCH:
		kindStr = "AFTERTOUCH"
	case CONTROLLER:
		kindStr = "CONTROLLER"
	case SYSTEM:
		kindStr = "SYSTEM"
	}
	if kindStr == "other" {
		return fmt.Sprintf("(%#x ch=%v key=%v val=%v)", m.Kind, m.Channel, m.Key, m.Value)
	} else {
		return fmt.Sprintf("(%s ch=%v key=%v val=%v)", kindStr, m.Channel, m.Key, m.Value)
	}
}

//================================================================================
// PARSE MIDI BYTES INTO MESSAGE OBJECTS

// Read a stream of raw MIDI bytes on inCh, parse them into *MidiMessage structs,
// and send over outCh.
// It accepts CLOCK, START, and STOP system messages but other system messages
// are ignored.
func MidiStreamParserThread(inCh chan byte, outCh chan *MidiMessage) {
	debug("starting thread")
	message := new(MidiMessage) // pointer
	ii := 0
	for b := range inCh {
		debug("")
		debug(fmt.Sprintf("ii = %v, got byte %v", ii, b))
		debug(fmt.Sprintf("current message = %v", message))

		// special handling for control bytes which mark the start of messages
		if b >= 128 {
			debug("     this is a control byte")
			message = new(MidiMessage)
			message.Kind = b & 0xf0
			message.Channel = b & 0x0f
			ii = 1
			if message.Kind == SYSTEM && (message.Channel == CLOCK || message.Channel == START || message.Channel == STOP) {
				debug("sending")
				outCh <- message
				ii = 0
			}
			continue
		}

		// handle data bytes differently depending on what number they are in a message
		debug("     this is a data byte")
		if ii == 0 {
			// we should never get a data byte at ii = 0.
			// if it happens it means we're not understanding this part of the stream,
			// so drop the byte and do nothing until we get another control byte.
			continue
		} else if ii == 1 {
			debug("     1")
			message.Key = b
			// these kinds expect 1 data byte, so we might be done:
			if message.Kind == PROGRAM_CHANGE || message.Kind == CHANNEL_PRESSURE {
				debug("sending")
				outCh <- message
				ii = 0
				continue
			}
		} else if ii == 2 {
			debug("     2")
			message.Value = b
			// these kinds expect 2 data bytes, so we might be done:
			if message.Kind == NOTE_OFF || message.Kind == NOTE_ON || message.Kind == AFTERTOUCH || message.Kind == CONTROLLER || message.Kind == PITCH_BEND {
				debug("sending")
				outCh <- message
				ii = 0
				continue
			}
		} else {
			// we're ignoring messages with more than 2 data bytes.
			// do nothing, wait for another control byte
		}

		ii += 1
	}

	// if we get here, inCh has been closed
	debug(" thread is done")
	close(outCh)
}

//================================================================================
// HELPERS

// Start some threads which will read and parse incoming MIDI messages in the background.
// Return a channel which emits pointers to MidiMessage structs.
// "path" should be the path to the midi device, e.g. "/dev/midi1".
// If the path can't be opened, it will keep retrying once a second forever until it succeeds.
func GetMidiMessageStream(path string) chan *MidiMessage {
	midiByteChan := make(chan byte, 3000)
	midiMessageChan := make(chan *MidiMessage, 500)

	go tenaciousFileByteStreamerThread(path, midiByteChan)
	go MidiStreamParserThread(midiByteChan, midiMessageChan)

	return midiMessageChan
}

// Stream the bytes from the given path, one byte at a time
// Assumes the file is a special device file which will never hit EOF.
// If the file can't be opened, it will keep trying once a second forver until it succeeds.
func tenaciousFileByteStreamerThread(path string, outCh chan byte) {
	for {
		file, err := os.Open(path)
		if err != nil {
			time.Sleep(time.Duration(RETRY_WAIT * time.Second))
			fmt.Println("[midi] couldn't open midi device:", path, " ... waiting and trying again")
			continue
		}
		defer file.Close()
		fmt.Println("[midi] successfully opened midi device", path)

		buf := make([]byte, 1)
		for {
			count, err := file.Read(buf)
			if err != nil {
				fmt.Println("[midi] couldn't read from midi device:", path)
				fmt.Println(err)
				break
			}
			if count != 1 {
				panic(fmt.Sprintf("[midi] ERROR: when reading, read %s bytes instead of 1", count))
			}
			outCh <- buf[0]
		}
	}
}

//================================================================================
// MIDISTATE TYPE

// Keeps track of the current state of the keys and controllers.
type MidiState struct {
	KeyVolumes         [128]byte      // values from 0 to 127
	ControllerValues   [128]byte      // values from 0 to 127
	RecentMidiMessages []*MidiMessage // midi messages from the most recent call to UpdateStateXXX()
}

// Pull all the available MidiMessages out of the channel without blocking.  Requires a channel
// with a buffer length greater than zero.
// This can deadlock if used by more than one goroutine at a time pulling on the same channel.
func GetAvailableMidiMessages(midiMessageChan chan *MidiMessage) []*MidiMessage {
	result := make([]*MidiMessage, 0)
	for {
		if len(midiMessageChan) == 0 {
			break
		}
		result = append(result, <-midiMessageChan)
	}
	return result
}

// Given a channel of MidiMessages, read all the messages that are available right now
// and update the MidiState object.
// Requires a channel that has a buffer length greater than zero.
func (midiState *MidiState) UpdateStateFromChannel(midiMessageChan chan *MidiMessage) {
	midiState.UpdateStateFromSlice(GetAvailableMidiMessages(midiMessageChan))
}

// Given a slice of MidiMessages, update the MidiState object.
// Note that the MidiState object will keep a pointer to the midiMessages
// object you provide here (as MidiState.RecentMidiMessages).
func (midiState *MidiState) UpdateStateFromSlice(midiMessages []*MidiMessage) {
	midiState.RecentMidiMessages = midiMessages
	for _, m := range midiState.RecentMidiMessages {
		switch m.Kind {
		case NOTE_OFF:
			midiState.KeyVolumes[m.Key] = 0
		case NOTE_ON:
			midiState.KeyVolumes[m.Key] = m.Value
		case CONTROLLER:
			midiState.ControllerValues[m.Key] = m.Value
		}
	}
}
<commit_msg>Read more MIDI bytes at once<commit_after>/*
Package midi allows you to listen to incoming MIDI messages.

Warning!  This is not a feature-complete MIDI implementation.  MIDI system messages are ignored with the exception of CLOCK, START, and STOP messages.

Example

If you already have a main event loop, do this:

 // Launch the MIDI reader thread and get the channel it uses to deliver incoming messages
 midiMessageChan := midi.GetMidiMessageStream("/dev/midi1")

 // Create a persistent object which knows the current MIDI state
 // This includes the current volumes of all notes (will be 0 for non-playing notes)
 // and the current value of all controllers.
 midiState := midi.MidiState{}

 // Your main event loop
 for {
     // Nonblockingly get all the MIDI messages that have come in since we last checked
     // and use them to update the MIDI state
     midiState.UpdateStateFromChannel(midiMessageChan)

     // Do things with the MIDI data
     if midiState.KeyVolumes[60] > 0 {
         fmt.Println("Key 60 is held down right now")
     }
 }

For more details on MIDI:

http://www.midi.org/techspecs/midimessages.php

https://en.wikipedia.org/wiki/MIDI_timecode#Quarter-frame_messages

*/
package midi

import (
	"fmt"
	"os"
	"time"
)

//================================================================================
// CONSTANTS

const RETRY_WAIT = 2 // seconds to wait before retrying opening midi device file

// kinds of messages

const NOTE_OFF = byte(0x80)
const NOTE_ON = byte(0x90)
const AFTERTOUCH = byte(0xa0)
const CONTROLLER = byte(0xb0)
const PROGRAM_CHANGE = byte(0xc0)
const CHANNEL_PRESSURE = byte(0xd0)
const PITCH_BEND = byte(0xe0)
const SYSTEM = byte(0xf0)

// special channel numbers for SYSTEM messages

const CLOCK = byte(8)
const START = byte(10)
const STOP = byte(12)

//================================================================================
// MIDIMESSAGE TYPE

type MidiMessage struct {
	Kind    byte // one of the constants above
	Channel byte // either a channel number of one of the special channel constants CLOCK, START, STOP
	Key     byte // key, controller, instrument, or pitch bend lsb
	Value   byte // velocity, touch, controller value, channel pressure, or pitch bend msb
}

func debug(s string) {
	//fmt.Println("    [midi]", s)
}

// Convert the MidiMessage to a human-readable string so it can be printed
func (m *MidiMessage) String() string {
	kindStr := "other"
	switch m.Kind {
	case NOTE_OFF:
		kindStr = "NOTE_OFF"
	case NOTE_ON:
		kindStr = "NOTE_ON"
	case AFTERTOUCH:
		kindStr = "AFTERTOUCH"
	case CONTROLLER:
		kindStr = "CONTROLLER"
	case SYSTEM:
		kindStr = "SYSTEM"
	}
	if kindStr == "other" {
		return fmt.Sprintf("(%#x ch=%v key=%v val=%v)", m.Kind, m.Channel, m.Key, m.Value)
	} else {
		return fmt.Sprintf("(%s ch=%v key=%v val=%v)", kindStr, m.Channel, m.Key, m.Value)
	}
}

//================================================================================
// PARSE MIDI BYTES INTO MESSAGE OBJECTS

// Read a stream of raw MIDI bytes on inCh, parse them into *MidiMessage structs,
// and send over outCh.
// It accepts CLOCK, START, and STOP system messages but other system messages
// are ignored.
func MidiStreamParserThread(inCh chan byte, outCh chan *MidiMessage) {
	debug("starting thread")
	message := new(MidiMessage) // pointer
	ii := 0
	for b := range inCh {
		debug("")
		debug(fmt.Sprintf("ii = %v, got byte %v", ii, b))
		debug(fmt.Sprintf("current message = %v", message))

		// special handling for control bytes which mark the start of messages
		if b >= 128 {
			debug("     this is a control byte")
			message = new(MidiMessage)
			message.Kind = b & 0xf0
			message.Channel = b & 0x0f
			ii = 1
			if message.Kind == SYSTEM && (message.Channel == CLOCK || message.Channel == START || message.Channel == STOP) {
				debug("sending")
				outCh <- message
				ii = 0
			}
			continue
		}

		// handle data bytes differently depending on what number they are in a message
		debug("     this is a data byte")
		if ii == 0 {
			// we should never get a data byte at ii = 0.
			// if it happens it means we're not understanding this part of the stream,
			// so drop the byte and do nothing until we get another control byte.
			continue
		} else if ii == 1 {
			debug("     1")
			message.Key = b
			// these kinds expect 1 data byte, so we might be done:
			if message.Kind == PROGRAM_CHANGE || message.Kind == CHANNEL_PRESSURE {
				debug("sending")
				outCh <- message
				ii = 0
				continue
			}
		} else if ii == 2 {
			debug("     2")
			message.Value = b
			// these kinds expect 2 data bytes, so we might be done:
			if message.Kind == NOTE_OFF || message.Kind == NOTE_ON || message.Kind == AFTERTOUCH || message.Kind == CONTROLLER || message.Kind == PITCH_BEND {
				debug("sending")
				outCh <- message
				ii = 0
				continue
			}
		} else {
			// we're ignoring messages with more than 2 data bytes.
			// do nothing, wait for another control byte
		}

		ii += 1
	}

	// if we get here, inCh has been closed
	debug(" thread is done")
	close(outCh)
}

//================================================================================
// HELPERS

// Start some threads which will read and parse incoming MIDI messages in the background.
// Return a channel which emits pointers to MidiMessage structs.
// "path" should be the path to the midi device, e.g. "/dev/midi1".
// If the path can't be opened, it will keep retrying once a second forever until it succeeds.
func GetMidiMessageStream(path string) chan *MidiMessage {
	midiByteChan := make(chan byte, 3000)
	midiMessageChan := make(chan *MidiMessage, 500)

	go tenaciousFileByteStreamerThread(path, midiByteChan)
	go MidiStreamParserThread(midiByteChan, midiMessageChan)

	return midiMessageChan
}

// Stream the bytes from the given path, one byte at a time
// Assumes the file is a special device file which will never hit EOF.
// If the file can't be opened, it will keep trying once a second forver until it succeeds.
func tenaciousFileByteStreamerThread(path string, outCh chan byte) {
	for {
		file, err := os.Open(path)
		if err != nil {
			time.Sleep(time.Duration(RETRY_WAIT * time.Second))
			fmt.Println("[midi] couldn't open midi device:", path, " ... waiting and trying again")
			continue
		}
		defer file.Close()
		fmt.Println("[midi] successfully opened midi device", path)

		buf := make([]byte, 1024)
		for {
			count, err := file.Read(buf)
			if err != nil {
				fmt.Println("[midi] couldn't read from midi device:", path)
				fmt.Println(err)
				break
			}
            for ii := 0; ii < count; ii++ {
                outCh <- buf[ii]
            }
		}
	}
}

//================================================================================
// MIDISTATE TYPE

// Keeps track of the current state of the keys and controllers.
type MidiState struct {
	KeyVolumes         [128]byte      // values from 0 to 127
	ControllerValues   [128]byte      // values from 0 to 127
	RecentMidiMessages []*MidiMessage // midi messages from the most recent call to UpdateStateXXX()
}

// Pull all the available MidiMessages out of the channel without blocking.  Requires a channel
// with a buffer length greater than zero.
// This can deadlock if used by more than one goroutine at a time pulling on the same channel.
func GetAvailableMidiMessages(midiMessageChan chan *MidiMessage) []*MidiMessage {
	result := make([]*MidiMessage, 0)
	for {
		if len(midiMessageChan) == 0 {
			break
		}
		result = append(result, <-midiMessageChan)
	}
	return result
}

// Given a channel of MidiMessages, read all the messages that are available right now
// and update the MidiState object.
// Requires a channel that has a buffer length greater than zero.
func (midiState *MidiState) UpdateStateFromChannel(midiMessageChan chan *MidiMessage) {
	midiState.UpdateStateFromSlice(GetAvailableMidiMessages(midiMessageChan))
}

// Given a slice of MidiMessages, update the MidiState object.
// Note that the MidiState object will keep a pointer to the midiMessages
// object you provide here (as MidiState.RecentMidiMessages).
func (midiState *MidiState) UpdateStateFromSlice(midiMessages []*MidiMessage) {
	midiState.RecentMidiMessages = midiMessages
	for _, m := range midiState.RecentMidiMessages {
		switch m.Kind {
		case NOTE_OFF:
			midiState.KeyVolumes[m.Key] = 0
		case NOTE_ON:
			midiState.KeyVolumes[m.Key] = m.Value
		case CONTROLLER:
			midiState.ControllerValues[m.Key] = m.Value
		}
	}
}
<|endoftext|>


<commit_before>package plugins

import (
	seabird ".."

	"encoding/json"
)

type DownPlugin struct {
	Bot *seabird.Bot
}

func init() {
	seabird.RegisterPlugin("down", NewChancePlugin)
}

func NewDownPlugin(b *seabird.Bot, c json.RawMessage) {
	p := &ChancePlugin{Bot: b}

	/*
		err := json.Unmarshal(c, &p.Key)
		if err != nil {
			fmt.Println(err)
		}
	*/

	b.RegisterFunction("coin", p.CoinKick)
	//b.RegisterFunction("roulette", p.ForecastCurrent)
}
<commit_msg>remove unused plugin file<commit_after><|endoftext|>


<commit_before>// Package metric implements the queue metric for NSQ (http://nsq.io/).
package metric

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"os"
	"time"
)

const constNSQStatsEndpoint string = "127.0.0.1:4151"
const constNSQStatsAPI string = "/stats?format=json"

// compile-time assert that we implement the right interface
var _ Metric = (*NSQMetric)(nil)

// NSQMetric stores the current value.
type NSQMetric struct {
	currentVal  int
	topicName   string
	channelName string
}

// StatsMessage from NSQ stats API.
type StatsMessage struct {
	Data StatsData `json:"data"`
}

// StatsData from NSQ stats API.
type StatsData struct {
	Topics []Topic `json:"topics"`
}

// Topic from NSQ stats API.
type Topic struct {
	TopicName string    `json:"topic_name"`
	Channels  []Channel `json:"channels"`
}

// Channel from NSQ stats API.
type Channel struct {
	ChannelName string `json:"channel_name"`
	Depth       int    `json:"depth"`
}

var (
	httpClient = &http.Client{
		// TODO Make timeout configurable.
		Timeout: 10 * time.Second,
	}
	nsqStatsEndpoint string
	nsqInitialized   = false
)

// NSQInit sets up the NSQ Stats endpoint.
func NSQInit() {
	nsqStatsEndpoint = os.Getenv("NSQ_STATS_ENDPOINT")
	if nsqStatsEndpoint == "" {
		nsqStatsEndpoint = constNSQStatsEndpoint
	}

	nsqInitialized = true
}

// NewNSQMetric creates the metric.
func NewNSQMetric(topicName string, channelName string) *NSQMetric {
	if !nsqInitialized {
		NSQInit()
	}

	return &NSQMetric{
		topicName:   topicName,
		channelName: channelName,
	}
}

// Call NSQ Stats API to get the queue length.
func getJSONGet(url string) (body []byte, err error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		log.Errorf("Failed to build API GET request err %v", err)
		return nil, err
	}

	req.Header.Set("Content-Type", "application/json")
	resp, err := httpClient.Do(req)
	if err != nil {
		log.Errorf("Failed to GET err %v", err)
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		log.Errorf("Http error %d: %s", resp.StatusCode, resp.Status)
	}

	body, err = ioutil.ReadAll(resp.Body)

	return body, err
}

// UpdateCurrent sets the current queue length.
func (nsqm *NSQMetric) UpdateCurrent() {
	var statsMessage StatsMessage

	url := "http://" + nsqStatsEndpoint + constNSQStatsAPI
	body, err := getJSONGet(url)
	if err != nil {
		log.Errorf("Error getting NSQ metric %v", err)
	}

	err = json.Unmarshal(body, &statsMessage)
	if err != nil {
		log.Errorf("Error %v unmarshalling from %s", err, string(body[:]))
	}

	// Loop through NSQ Channels and Metrics to find the correct value.
	for _, topic := range statsMessage.Data.Topics {
		if topic.TopicName == nsqm.topicName {
			for _, channel := range topic.Channels {
				if channel.ChannelName == nsqm.channelName {
					nsqm.currentVal = channel.Depth
				}
			}
		}
	}

	log.Debugf("Topic: %s Channel: %s Length: %d", nsqm.topicName, nsqm.channelName, nsqm.currentVal)
}

// Current returns the queue length.
func (nsqm *NSQMetric) Current() int {
	return nsqm.currentVal
}
<commit_msg>Adding missing return to NSQInit.<commit_after>// Package metric implements the queue metric for NSQ (http://nsq.io/).
package metric

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"os"
	"time"
)

const constNSQStatsEndpoint string = "127.0.0.1:4151"
const constNSQStatsAPI string = "/stats?format=json"

// compile-time assert that we implement the right interface
var _ Metric = (*NSQMetric)(nil)

// NSQMetric stores the current value.
type NSQMetric struct {
	currentVal  int
	topicName   string
	channelName string
}

// StatsMessage from NSQ stats API.
type StatsMessage struct {
	Data StatsData `json:"data"`
}

// StatsData from NSQ stats API.
type StatsData struct {
	Topics []Topic `json:"topics"`
}

// Topic from NSQ stats API.
type Topic struct {
	TopicName string    `json:"topic_name"`
	Channels  []Channel `json:"channels"`
}

// Channel from NSQ stats API.
type Channel struct {
	ChannelName string `json:"channel_name"`
	Depth       int    `json:"depth"`
}

var (
	httpClient = &http.Client{
		// TODO Make timeout configurable.
		Timeout: 10 * time.Second,
	}
	nsqStatsEndpoint string
	nsqInitialized   = false
)

// NSQInit sets up the NSQ Stats endpoint.
func NSQInit() {
	nsqStatsEndpoint = os.Getenv("NSQ_STATS_ENDPOINT")
	if nsqStatsEndpoint == "" {
		nsqStatsEndpoint = constNSQStatsEndpoint
	}

	nsqInitialized = true
	return
}

// NewNSQMetric creates the metric.
func NewNSQMetric(topicName string, channelName string) *NSQMetric {
	if !nsqInitialized {
		NSQInit()
	}

	return &NSQMetric{
		topicName:   topicName,
		channelName: channelName,
	}
}

// Call NSQ Stats API to get the queue length.
func getJSONGet(url string) (body []byte, err error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		log.Errorf("Failed to build API GET request err %v", err)
		return nil, err
	}

	req.Header.Set("Content-Type", "application/json")
	resp, err := httpClient.Do(req)
	if err != nil {
		log.Errorf("Failed to GET err %v", err)
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		log.Errorf("Http error %d: %s", resp.StatusCode, resp.Status)
	}

	body, err = ioutil.ReadAll(resp.Body)

	return body, err
}

// UpdateCurrent sets the current queue length.
func (nsqm *NSQMetric) UpdateCurrent() {
	var statsMessage StatsMessage

	url := "http://" + nsqStatsEndpoint + constNSQStatsAPI
	body, err := getJSONGet(url)
	if err != nil {
		log.Errorf("Error getting NSQ metric %v", err)
	}

	err = json.Unmarshal(body, &statsMessage)
	if err != nil {
		log.Errorf("Error %v unmarshalling from %s", err, string(body[:]))
	}

	// Loop through NSQ Channels and Metrics to find the correct value.
	for _, topic := range statsMessage.Data.Topics {
		if topic.TopicName == nsqm.topicName {
			for _, channel := range topic.Channels {
				if channel.ChannelName == nsqm.channelName {
					nsqm.currentVal = channel.Depth
				}
			}
		}
	}

	log.Debugf("Topic: %s Channel: %s Length: %d", nsqm.topicName, nsqm.channelName, nsqm.currentVal)
}

// Current returns the queue length.
func (nsqm *NSQMetric) Current() int {
	return nsqm.currentVal
}
<|endoftext|>


<commit_before>package iptables

import (
	"fmt"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/godbus/dbus"
)

// IPV defines the table string
type IPV string

const (
	// Iptables point ipv4 table
	Iptables IPV = "ipv4"
	// IP6Tables point to ipv6 table
	IP6Tables IPV = "ipv6"
	// Ebtables point to bridge table
	Ebtables IPV = "eb"
)
const (
	dbusInterface = "org.fedoraproject.FirewallD1"
	dbusPath      = "/org/fedoraproject/FirewallD1"
)

// Conn is a connection to firewalld dbus endpoint.
type Conn struct {
	sysconn *dbus.Conn
	sysobj  dbus.BusObject
	signal  chan *dbus.Signal
}

var (
	connection       *Conn
	firewalldRunning bool      // is Firewalld service running
	onReloaded       []*func() // callbacks when Firewalld has been reloaded
)

// FirewalldInit initializes firewalld management code.
func FirewalldInit() error {
	var err error

	if connection, err = newConnection(); err != nil {
		return fmt.Errorf("Failed to connect to D-Bus system bus: %v", err)
	}
	if connection != nil {
		go signalHandler()
	}

	firewalldRunning = checkRunning()
	return nil
}

// New() establishes a connection to the system bus.
func newConnection() (*Conn, error) {
	c := new(Conn)
	if err := c.initConnection(); err != nil {
		return nil, err
	}

	return c, nil
}

// Innitialize D-Bus connection.
func (c *Conn) initConnection() error {
	var err error

	c.sysconn, err = dbus.SystemBus()
	if err != nil {
		return err
	}

	// This never fails, even if the service is not running atm.
	c.sysobj = c.sysconn.Object(dbusInterface, dbus.ObjectPath(dbusPath))

	rule := fmt.Sprintf("type='signal',path='%s',interface='%s',sender='%s',member='Reloaded'",
		dbusPath, dbusInterface, dbusInterface)
	c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)

	rule = fmt.Sprintf("type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',path='/org/freedesktop/DBus',sender='org.freedesktop.DBus',arg0='%s'",
		dbusInterface)
	c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)

	c.signal = make(chan *dbus.Signal, 10)
	c.sysconn.Signal(c.signal)

	return nil
}

func signalHandler() {
	for signal := range connection.signal {
		if strings.Contains(signal.Name, "NameOwnerChanged") {
			firewalldRunning = checkRunning()
			dbusConnectionChanged(signal.Body)
		} else if strings.Contains(signal.Name, "Reloaded") {
			reloaded()
		}
	}
}

func dbusConnectionChanged(args []interface{}) {
	name := args[0].(string)
	oldOwner := args[1].(string)
	newOwner := args[2].(string)

	if name != dbusInterface {
		return
	}

	if len(newOwner) > 0 {
		connectionEstablished()
	} else if len(oldOwner) > 0 {
		connectionLost()
	}
}

func connectionEstablished() {
	reloaded()
}

func connectionLost() {
	// Doesn't do anything for now. Libvirt also doesn't react to this.
}

// call all callbacks
func reloaded() {
	for _, pf := range onReloaded {
		(*pf)()
	}
}

// OnReloaded add callback
func OnReloaded(callback func()) {
	for _, pf := range onReloaded {
		if pf == &callback {
			return
		}
	}
	onReloaded = append(onReloaded, &callback)
}

// Call some remote method to see whether the service is actually running.
func checkRunning() bool {
	var zone string
	var err error

	if connection != nil {
		err = connection.sysobj.Call(dbusInterface+".getDefaultZone", 0).Store(&zone)
		logrus.Infof("Firewalld running: %t", err == nil)
		return err == nil
	}
	return false
}

// Passthrough method simply passes args through to iptables/ip6tables
func Passthrough(ipv IPV, args ...string) ([]byte, error) {
	var output string
	logrus.Debugf("Firewalld passthrough: %s, %s", ipv, args)
	if err := connection.sysobj.Call(dbusInterface+".direct.passthrough", 0, ipv, args).Store(&output); err != nil {
		return nil, err
	}
	return []byte(output), nil
}
<commit_msg>Close dbus connection if firewalld is not started<commit_after>package iptables

import (
	"fmt"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/godbus/dbus"
)

// IPV defines the table string
type IPV string

const (
	// Iptables point ipv4 table
	Iptables IPV = "ipv4"
	// IP6Tables point to ipv6 table
	IP6Tables IPV = "ipv6"
	// Ebtables point to bridge table
	Ebtables IPV = "eb"
)
const (
	dbusInterface = "org.fedoraproject.FirewallD1"
	dbusPath      = "/org/fedoraproject/FirewallD1"
)

// Conn is a connection to firewalld dbus endpoint.
type Conn struct {
	sysconn *dbus.Conn
	sysobj  dbus.BusObject
	signal  chan *dbus.Signal
}

var (
	connection       *Conn
	firewalldRunning bool      // is Firewalld service running
	onReloaded       []*func() // callbacks when Firewalld has been reloaded
)

// FirewalldInit initializes firewalld management code.
func FirewalldInit() error {
	var err error

	if connection, err = newConnection(); err != nil {
		return fmt.Errorf("Failed to connect to D-Bus system bus: %v", err)
	}
	firewalldRunning = checkRunning()
	if !firewalldRunning {
		connection.sysconn.Close()
		connection = nil
	}
	if connection != nil {
		go signalHandler()
	}

	return nil
}

// New() establishes a connection to the system bus.
func newConnection() (*Conn, error) {
	c := new(Conn)
	if err := c.initConnection(); err != nil {
		return nil, err
	}

	return c, nil
}

// Innitialize D-Bus connection.
func (c *Conn) initConnection() error {
	var err error

	c.sysconn, err = dbus.SystemBus()
	if err != nil {
		return err
	}

	// This never fails, even if the service is not running atm.
	c.sysobj = c.sysconn.Object(dbusInterface, dbus.ObjectPath(dbusPath))

	rule := fmt.Sprintf("type='signal',path='%s',interface='%s',sender='%s',member='Reloaded'",
		dbusPath, dbusInterface, dbusInterface)
	c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)

	rule = fmt.Sprintf("type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',path='/org/freedesktop/DBus',sender='org.freedesktop.DBus',arg0='%s'",
		dbusInterface)
	c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)

	c.signal = make(chan *dbus.Signal, 10)
	c.sysconn.Signal(c.signal)

	return nil
}

func signalHandler() {
	for signal := range connection.signal {
		if strings.Contains(signal.Name, "NameOwnerChanged") {
			firewalldRunning = checkRunning()
			dbusConnectionChanged(signal.Body)
		} else if strings.Contains(signal.Name, "Reloaded") {
			reloaded()
		}
	}
}

func dbusConnectionChanged(args []interface{}) {
	name := args[0].(string)
	oldOwner := args[1].(string)
	newOwner := args[2].(string)

	if name != dbusInterface {
		return
	}

	if len(newOwner) > 0 {
		connectionEstablished()
	} else if len(oldOwner) > 0 {
		connectionLost()
	}
}

func connectionEstablished() {
	reloaded()
}

func connectionLost() {
	// Doesn't do anything for now. Libvirt also doesn't react to this.
}

// call all callbacks
func reloaded() {
	for _, pf := range onReloaded {
		(*pf)()
	}
}

// OnReloaded add callback
func OnReloaded(callback func()) {
	for _, pf := range onReloaded {
		if pf == &callback {
			return
		}
	}
	onReloaded = append(onReloaded, &callback)
}

// Call some remote method to see whether the service is actually running.
func checkRunning() bool {
	var zone string
	var err error

	if connection != nil {
		err = connection.sysobj.Call(dbusInterface+".getDefaultZone", 0).Store(&zone)
		logrus.Infof("Firewalld running: %t", err == nil)
		return err == nil
	}
	return false
}

// Passthrough method simply passes args through to iptables/ip6tables
func Passthrough(ipv IPV, args ...string) ([]byte, error) {
	var output string
	logrus.Debugf("Firewalld passthrough: %s, %s", ipv, args)
	if err := connection.sysobj.Call(dbusInterface+".direct.passthrough", 0, ipv, args).Store(&output); err != nil {
		return nil, err
	}
	return []byte(output), nil
}
<|endoftext|>


<commit_before>package main

import "fmt"
import "unsafe"
import "reflect"

/*
runtime.h
struct Iface
{
	Itab* tab;
	void* data;
};
struct Itab
{
	InterfaceType* inter;
	Type* type;
	void (*fun[])(void);
};
*/

type Stringer interface {
	String() string
}

type Printer interface {
	Stringer //embed intf
	Print()
}

type User struct {
	id   int
	name string
}

// implement interface
func (self *User) String() string {
	return fmt.Sprintf("user: %d, %s", self.id, self.name)
}

func (self *User) Print() {
	fmt.Println(self.String())
}

// empty interface. all type implemented.
func Print(v interface{}) {
	fmt.Printf("%T: %v\n", v, v)
}

type Tester struct {
	s interface { // anonymous interface as field
		String() string
	}
}

func main() {

	println("------------------------------")
	var t Printer = &User{1, "tom"} //(*User) methods conclude String() & Print()
	t.Print()

	println("------------------------------")
	Print(1)
	Print("Hello world!")

	println("------------------------------")
	tr := Tester{&User{2, "jack"}}
	fmt.Println(tr.s.String())

	println("------------------------------")
	u := User{3, "simba"}
	var vi, pi interface{} = u, &u
	// vi.(User).name = "SIMBA" // Error: cannot assign to vi.(User).name
	pi.(*User).name = "SIMBA"
	fmt.Printf("%v\n", vi.(User)) // vi is a readonly copy of u
	fmt.Printf("%v\n", pi.(*User))

	println("------------------------------")
	if i, ok := pi.(fmt.Stringer); ok {
		fmt.Println(i)
		println("it's ok....")
	}

	switch v := pi.(type) {
	case nil: // 0 == nil
		fmt.Println("nil")
	case fmt.Stringer: // interface
		fmt.Println(v)
	case func() string: //func
		fmt.Println(v())
	case *User: // *struct
		fmt.Printf("%d, %s\n", v.id, v.name)
	default:
		fmt.Println("unknown")

	}

	println("------------------------------")
	var a interface{} = nil         // tab = nil, data = nil
	var b interface{} = (*int)(nil) // tab contain *int typeinfo, data = nil
	type iface struct {
		itab, data uintptr
	}

	ia := *(*iface)(unsafe.Pointer(&a))
	ib := *(*iface)(unsafe.Pointer(&b))

	fmt.Println(a == nil, ia)
	fmt.Println(b == nil, ib, reflect.ValueOf(b).IsNil())
}
<commit_msg>add test_intf.go<commit_after>package main

import "fmt"
import "unsafe"
import "reflect"

/*
runtime.h
struct Iface
{
	Itab* tab;
	void* data;
};
struct Itab
{
	InterfaceType* inter;
	Type* type;
	void (*fun[])(void);
};
*/

type Stringer interface {
	String() string
}

type Printer interface {
	Stringer //embed intf
	Print()
}

type User struct {
	id   int
	name string
}

// implement interface
func (self *User) String() string {
	return fmt.Sprintf("user: %d, %s", self.id, self.name)
}

func (self *User) Print() {
	fmt.Println(self.String())
}

// empty interface. all type implemented.
func Print(v interface{}) {
	fmt.Printf("%T: %v\n", v, v)
}

type Tester struct {
	s interface { // anonymous interface as field
		String() string
	}
}

func main() {

	println("------------------------------")
	var t Printer = &User{1, "tom"} //(*User) methods conclude String() & Print()
	t.Print()

	println("------------------------------")
	Print(1)
	Print("Hello world!")

	println("------------------------------")
	tr := Tester{&User{2, "jack"}}
	fmt.Println(tr.s.String())

	println("------------------------------")
	u := User{3, "simba"}
	var vi, pi interface{} = u, &u
	// vi.(User).name = "SIMBA" // Error: cannot assign to vi.(User).name
	pi.(*User).name = "SIMBA"
	fmt.Printf("%v\n", vi.(User))  // vi is a readonly copy of u
	fmt.Printf("%v\n", pi.(*User)) // Printf will call u.String()

	println("------------------------------")
	if i, ok := pi.(fmt.Stringer); ok {
		fmt.Println(i)
		println("it's ok....")
	}

	switch v := pi.(type) {
	case nil: // 0 == nil
		fmt.Println("nil")
	case fmt.Stringer: // interface
		fmt.Println(v)
	case func() string: //func
		fmt.Println(v())
	case *User: // *struct
		fmt.Printf("%d, %s\n", v.id, v.name)
	default:
		fmt.Println("unknown")

	}

	println("------------------------------")
	var a interface{} = nil         // tab = nil, data = nil
	var b interface{} = (*int)(nil) // tab contain *int typeinfo, data = nil
	type iface struct {
		itab, data uintptr
	}

	ia := *(*iface)(unsafe.Pointer(&a))
	ib := *(*iface)(unsafe.Pointer(&b))

	fmt.Println(a == nil, ia)
	fmt.Println(b == nil, ib, reflect.ValueOf(b).IsNil())
}
<|endoftext|>