<commit_before>package host

import (
	"errors"
	"log"
	"net"
	"sync"

	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/modules"
	safesync "github.com/NebulousLabs/Sia/sync"
	"github.com/NebulousLabs/Sia/types"
)

const (
	// StorageProofReorgDepth states how many blocks to wait before submitting
	// a storage proof. This reduces the chance of needing to resubmit because
	// of a reorg.
	StorageProofReorgDepth = 10
	maxContractLen         = 1 << 16 // The maximum allowed size of a file contract coming in over the wire. This does not include the file.
)

var (
	defaultPrice = types.SiacoinPrecision.Div(types.NewCurrency64(4320e9 / 200)) // 200 SC / GB / Month
)

// A contractObligation tracks a file contract that the host is obligated to
// fulfill.
type contractObligation struct {
	ID              types.FileContractID
	FileContract    types.FileContract
	LastRevisionTxn types.Transaction
	Path            string // Where on disk the file is stored.

	// each obligation needs a mutex to prevent simultaneous revisions to the
	// same obligation
	mu *sync.Mutex
}

// A Host contains all the fields necessary for storing files for clients and
// performing the storage proofs on the received files.
type Host struct {
	// modules
	cs     modules.ConsensusSet
	hostdb modules.HostDB
	tpool  modules.TransactionPool
	wallet modules.Wallet

	// resources
	listener net.Listener
	log      *log.Logger

	// variables
	blockHeight         types.BlockHeight
	obligationsByID     map[types.FileContractID]contractObligation
	obligationsByHeight map[types.BlockHeight][]contractObligation
	spaceRemaining      int64
	fileCounter         int
	profit              types.Currency
	modules.HostSettings

	// constants
	myAddr     modules.NetAddress
	persistDir string
	secretKey  crypto.SecretKey
	publicKey  types.SiaPublicKey

	mu *safesync.RWMutex
}

// New returns an initialized Host.
func New(cs modules.ConsensusSet, hdb modules.HostDB, tpool modules.TransactionPool, wallet modules.Wallet, addr string, persistDir string) (*Host, error) {
	if cs == nil {
		return nil, errors.New("host cannot use a nil state")
	}
	if hdb == nil {
		return nil, errors.New("host cannot use a nil hostdb")
	}
	if tpool == nil {
		return nil, errors.New("host cannot use a nil tpool")
	}
	if wallet == nil {
		return nil, errors.New("host cannot use a nil wallet")
	}

	h := &Host{
		cs:     cs,
		hostdb: hdb,
		tpool:  tpool,
		wallet: wallet,

		// default host settings
		HostSettings: modules.HostSettings{
			TotalStorage: 10e9,         // 10 GB
			MaxFilesize:  100e9,        // 100 GB
			MaxDuration:  144 * 60,     // 60 days
			WindowSize:   288,          // 48 hours
			Price:        defaultPrice, // 200 SC / GB / Month
			Collateral:   types.NewCurrency64(0),
		},

		persistDir: persistDir,

		obligationsByID:     make(map[types.FileContractID]contractObligation),
		obligationsByHeight: make(map[types.BlockHeight][]contractObligation),

		mu: safesync.New(modules.SafeMutexDelay, 1),
	}
	h.spaceRemaining = h.TotalStorage

	// Generate signing key, for revising contracts.
	sk, pk, err := crypto.StdKeyGen.Generate()
	if err != nil {
		return nil, err
	}
	h.secretKey = sk
	h.publicKey = types.SiaPublicKey{
		Algorithm: types.SignatureEd25519,
		Key:       pk[:],
	}

	// Load the old host data and initialize the logger.
	err = h.initPersist()
	if err != nil {
		return nil, err
	}

	// Create listener and set address.
	h.listener, err = net.Listen("tcp", addr)
	if err != nil {
		return nil, err
	}
	h.myAddr = modules.NetAddress(h.listener.Addr().String())

	// Forward the hosting port, if possible.
	go h.forwardPort(h.myAddr.Port())

	// Learn our external IP.
	go h.learnHostname()

	// spawn listener
	go h.listen()

	h.cs.ConsensusSetSubscribe(h)

	return h, nil
}

// SetConfig updates the host's internal HostSettings object. To modify
// a specific field, use a combination of Info and SetConfig
func (h *Host) SetSettings(settings modules.HostSettings) {
	lockID := h.mu.Lock()
	defer h.mu.Unlock(lockID)
	h.spaceRemaining += settings.TotalStorage - h.TotalStorage
	h.HostSettings = settings
	h.save()
}

// Settings returns the settings of a host.
func (h *Host) Settings() modules.HostSettings {
	lockID := h.mu.RLock()
	defer h.mu.RUnlock(lockID)
	return h.HostSettings
}

func (h *Host) Address() modules.NetAddress {
	// no lock needed; h.myAddr is only set once (in New).
	return h.myAddr
}

func (h *Host) Info() modules.HostInfo {
	lockID := h.mu.RLock()
	defer h.mu.RUnlock(lockID)

	info := modules.HostInfo{
		HostSettings: h.HostSettings,

		StorageRemaining: h.spaceRemaining,
		NumContracts:     len(h.obligationsByID),
		Profit:           h.profit,
	}
	// sum up the current obligations to calculate PotentialProfit
	for _, obligation := range h.obligationsByID {
		fc := obligation.FileContract
		info.PotentialProfit = info.PotentialProfit.Add(types.PostTax(h.blockHeight, fc.Payout))
	}

	// Calculate estimated competition (reported in per GB per month). Price
	// calculated by taking the average of hosts 8-15.
	var averagePrice types.Currency
	hosts := h.hostdb.RandomHosts(15)
	for i, host := range hosts {
		if i < 8 {
			continue
		}
		averagePrice = averagePrice.Add(host.Price)
	}
	if len(hosts) == 0 {
		return info
	}
	averagePrice = averagePrice.Div(types.NewCurrency64(uint64(len(hosts))))
	// HACK: 4320 is one month, and 1e9 is a GB. Price is reported as per GB
	// per month.
	estimatedCost := averagePrice.Mul(types.NewCurrency64(4320)).Mul(types.NewCurrency64(1e9))
	info.Competition = estimatedCost

	return info
}

// Close saves the state of the Gateway and stops its listener process.
func (h *Host) Close() error {
	id := h.mu.RLock()
	// save the latest host state
	if err := h.save(); err != nil {
		return err
	}
	h.mu.RUnlock(id)
	// clear the port mapping (no effect if UPnP not supported)
	h.clearPort(h.myAddr.Port())
	// shut down the listener
	return h.listener.Close()
}
<commit_msg>set host's Settings.IPAddress properly<commit_after>package host

import (
	"errors"
	"log"
	"net"
	"sync"

	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/modules"
	safesync "github.com/NebulousLabs/Sia/sync"
	"github.com/NebulousLabs/Sia/types"
)

const (
	// StorageProofReorgDepth states how many blocks to wait before submitting
	// a storage proof. This reduces the chance of needing to resubmit because
	// of a reorg.
	StorageProofReorgDepth = 10
	maxContractLen         = 1 << 16 // The maximum allowed size of a file contract coming in over the wire. This does not include the file.
)

var (
	defaultPrice = types.SiacoinPrecision.Div(types.NewCurrency64(4320e9 / 200)) // 200 SC / GB / Month
)

// A contractObligation tracks a file contract that the host is obligated to
// fulfill.
type contractObligation struct {
	ID              types.FileContractID
	FileContract    types.FileContract
	LastRevisionTxn types.Transaction
	Path            string // Where on disk the file is stored.

	// each obligation needs a mutex to prevent simultaneous revisions to the
	// same obligation
	mu *sync.Mutex
}

// A Host contains all the fields necessary for storing files for clients and
// performing the storage proofs on the received files.
type Host struct {
	// modules
	cs     modules.ConsensusSet
	hostdb modules.HostDB
	tpool  modules.TransactionPool
	wallet modules.Wallet

	// resources
	listener net.Listener
	log      *log.Logger

	// variables
	blockHeight         types.BlockHeight
	obligationsByID     map[types.FileContractID]contractObligation
	obligationsByHeight map[types.BlockHeight][]contractObligation
	spaceRemaining      int64
	fileCounter         int
	profit              types.Currency
	modules.HostSettings

	// constants
	myAddr     modules.NetAddress
	persistDir string
	secretKey  crypto.SecretKey
	publicKey  types.SiaPublicKey

	mu *safesync.RWMutex
}

// New returns an initialized Host.
func New(cs modules.ConsensusSet, hdb modules.HostDB, tpool modules.TransactionPool, wallet modules.Wallet, addr string, persistDir string) (*Host, error) {
	if cs == nil {
		return nil, errors.New("host cannot use a nil state")
	}
	if hdb == nil {
		return nil, errors.New("host cannot use a nil hostdb")
	}
	if tpool == nil {
		return nil, errors.New("host cannot use a nil tpool")
	}
	if wallet == nil {
		return nil, errors.New("host cannot use a nil wallet")
	}

	h := &Host{
		cs:     cs,
		hostdb: hdb,
		tpool:  tpool,
		wallet: wallet,

		// default host settings
		HostSettings: modules.HostSettings{
			TotalStorage: 10e9,         // 10 GB
			MaxFilesize:  100e9,        // 100 GB
			MaxDuration:  144 * 60,     // 60 days
			WindowSize:   288,          // 48 hours
			Price:        defaultPrice, // 200 SC / GB / Month
			Collateral:   types.NewCurrency64(0),
		},

		persistDir: persistDir,

		obligationsByID:     make(map[types.FileContractID]contractObligation),
		obligationsByHeight: make(map[types.BlockHeight][]contractObligation),

		mu: safesync.New(modules.SafeMutexDelay, 1),
	}
	h.spaceRemaining = h.TotalStorage

	// Generate signing key, for revising contracts.
	sk, pk, err := crypto.StdKeyGen.Generate()
	if err != nil {
		return nil, err
	}
	h.secretKey = sk
	h.publicKey = types.SiaPublicKey{
		Algorithm: types.SignatureEd25519,
		Key:       pk[:],
	}

	// Load the old host data and initialize the logger.
	err = h.initPersist()
	if err != nil {
		return nil, err
	}

	// Create listener and set address.
	h.listener, err = net.Listen("tcp", addr)
	if err != nil {
		return nil, err
	}
	h.myAddr = modules.NetAddress(h.listener.Addr().String())

	// Forward the hosting port, if possible.
	go h.forwardPort(h.myAddr.Port())

	// Learn our external IP.
	go h.learnHostname()

	// spawn listener
	go h.listen()

	h.cs.ConsensusSetSubscribe(h)

	return h, nil
}

// SetConfig updates the host's internal HostSettings object. To modify
// a specific field, use a combination of Info and SetConfig
func (h *Host) SetSettings(settings modules.HostSettings) {
	lockID := h.mu.Lock()
	defer h.mu.Unlock(lockID)
	h.spaceRemaining += settings.TotalStorage - h.TotalStorage
	h.HostSettings = settings
	h.save()
}

// Settings returns the settings of a host.
func (h *Host) Settings() modules.HostSettings {
	lockID := h.mu.RLock()
	defer h.mu.RUnlock(lockID)
	h.HostSettings.IPAddress = h.myAddr // needs to be updated manually
	return h.HostSettings
}

func (h *Host) Address() modules.NetAddress {
	// no lock needed; h.myAddr is only set once (in New).
	return h.myAddr
}

func (h *Host) Info() modules.HostInfo {
	lockID := h.mu.RLock()
	defer h.mu.RUnlock(lockID)

	h.HostSettings.IPAddress = h.myAddr // needs to be updated manually
	info := modules.HostInfo{
		HostSettings: h.HostSettings,

		StorageRemaining: h.spaceRemaining,
		NumContracts:     len(h.obligationsByID),
		Profit:           h.profit,
	}
	// sum up the current obligations to calculate PotentialProfit
	for _, obligation := range h.obligationsByID {
		fc := obligation.FileContract
		info.PotentialProfit = info.PotentialProfit.Add(types.PostTax(h.blockHeight, fc.Payout))
	}

	// Calculate estimated competition (reported in per GB per month). Price
	// calculated by taking the average of hosts 8-15.
	var averagePrice types.Currency
	hosts := h.hostdb.RandomHosts(15)
	for i, host := range hosts {
		if i < 8 {
			continue
		}
		averagePrice = averagePrice.Add(host.Price)
	}
	if len(hosts) == 0 {
		return info
	}
	averagePrice = averagePrice.Div(types.NewCurrency64(uint64(len(hosts))))
	// HACK: 4320 is one month, and 1e9 is a GB. Price is reported as per GB
	// per month.
	estimatedCost := averagePrice.Mul(types.NewCurrency64(4320)).Mul(types.NewCurrency64(1e9))
	info.Competition = estimatedCost

	return info
}

// Close saves the state of the Gateway and stops its listener process.
func (h *Host) Close() error {
	id := h.mu.RLock()
	// save the latest host state
	if err := h.save(); err != nil {
		return err
	}
	h.mu.RUnlock(id)
	// clear the port mapping (no effect if UPnP not supported)
	h.clearPort(h.myAddr.Port())
	// shut down the listener
	return h.listener.Close()
}
<|endoftext|>


<commit_before>package apply

import (
	"fmt"
	"strings"

	"encoding/json"

	"time"

	"regexp"

	"github.com/codegangsta/cli"
	"github.com/syou6162/go-active-learning/lib/cache"
	"github.com/syou6162/go-active-learning/lib/classifier"
	"github.com/syou6162/go-active-learning/lib/db"
	"github.com/syou6162/go-active-learning/lib/example"
	"github.com/syou6162/go-active-learning/lib/submodular"
	"github.com/syou6162/go-active-learning/lib/util"
)

var listName2Rule = map[string]*regexp.Regexp{
	"general":    regexp.MustCompile(`.+`),
	"github":     regexp.MustCompile(`https://github.com/.+`),
	"slideshare": regexp.MustCompile(`https://www.slideshare.net/.+`),
	"twitter":    regexp.MustCompile(`https://twitter.com/.+`),
	"arxiv":      regexp.MustCompile(`https://arxiv.org/abs/.+`),
}

func doApply(c *cli.Context) error {
	filterStatusCodeOk := c.Bool("filter-status-code-ok")
	jsonOutput := c.Bool("json-output")
	subsetSelection := c.Bool("subset-selection")
	sizeConstraint := c.Int("size-constraint")
	alpha := c.Float64("alpha")
	r := c.Float64("r")
	scoreThreshold := c.Float64("score-threshold")
	durationDay := c.Int64("duration-day")
	listName := c.String("listname")
	rule, ok := listName2Rule[listName]
	if ok == false {
		return cli.NewExitError("No matched rule", 1)
	}

	cache, err := cache.NewCache()
	if err != nil {
		return err
	}
	defer cache.Close()

	conn, err := db.CreateDBConnection()
	if err != nil {
		return err
	}
	defer conn.Close()

	examples, err := db.ReadLabeledExamples(conn, 10000)
	if err != nil {
		return err
	}

	cache.AttachMetaData(examples)
	if filterStatusCodeOk {
		examples = util.FilterStatusCodeOkExamples(examples)
	}
	model := classifier.NewBinaryClassifier(examples)

	targetExamples, err := db.ReadRecentExamples(conn, time.Now().Add(-time.Duration(24*durationDay)*time.Hour))
	if err != nil {
		return err
	}
	targetExamples = util.RemoveNegativeExamples(targetExamples)
	cache.AttachMetaData(targetExamples)

	result := example.Examples{}
	for _, e := range targetExamples {
		if !rule.MatchString(e.FinalUrl) {
			continue
		}
		e.Score = model.PredictScore(e.Fv)
		e.Title = strings.Replace(e.Title, "\n", " ", -1)
		if e.Score > scoreThreshold {
			result = append(result, e)
		}
	}

	if subsetSelection {
		result = submodular.SelectSubExamplesBySubModular(result, sizeConstraint, alpha, r)
	}

	err = cache.AddExamplesToList(listName, result)
	if err != nil {
		return err
	}

	for _, e := range result {
		if jsonOutput {
			b, err := json.Marshal(e)
			if err != nil {
				return err
			}
			fmt.Println(string(b))
		} else {
			fmt.Println(fmt.Sprintf("%0.03f\t%s", e.Score, e.Url))
		}
	}

	return nil
}

var CommandApply = cli.Command{
	Name:  "apply",
	Usage: "Apply classifier to unlabeled examples",
	Description: `
Apply classifier to unlabeled examples, and print a pair of score and url.
`,
	Action: doApply,
	Flags: []cli.Flag{
		cli.BoolFlag{Name: "filter-status-code-ok", Usage: "Use only examples with status code = 200"},
		cli.BoolFlag{Name: "json-output", Usage: "Make output with json format or not (tsv format)."},
		cli.BoolFlag{Name: "subset-selection", Usage: "Use subset selection algorithm (maximizing submodular function) to filter entries"},
		cli.Int64Flag{Name: "size-constraint", Value: 10, Usage: "Budget constraint. Max number of entries to be contained"},
		cli.Float64Flag{Name: "alpha", Value: 1.0},
		cli.Float64Flag{Name: "r", Value: 1.0, Usage: "Scaling factor for number of words"},
		cli.Float64Flag{Name: "score-threshold", Value: 0.0},
		cli.StringFlag{Name: "listname", Usage: "List name for cache"},
		cli.Int64Flag{Name: "duration-day", Usage: "Time span for fetching prediction target", Value: 2},
	},
}
<commit_msg>予測対象も200じゃないものは予測しないようにする<commit_after>package apply

import (
	"fmt"
	"strings"

	"encoding/json"

	"time"

	"regexp"

	"github.com/codegangsta/cli"
	"github.com/syou6162/go-active-learning/lib/cache"
	"github.com/syou6162/go-active-learning/lib/classifier"
	"github.com/syou6162/go-active-learning/lib/db"
	"github.com/syou6162/go-active-learning/lib/example"
	"github.com/syou6162/go-active-learning/lib/submodular"
	"github.com/syou6162/go-active-learning/lib/util"
)

var listName2Rule = map[string]*regexp.Regexp{
	"general":    regexp.MustCompile(`.+`),
	"github":     regexp.MustCompile(`https://github.com/.+`),
	"slideshare": regexp.MustCompile(`https://www.slideshare.net/.+`),
	"twitter":    regexp.MustCompile(`https://twitter.com/.+`),
	"arxiv":      regexp.MustCompile(`https://arxiv.org/abs/.+`),
}

func doApply(c *cli.Context) error {
	filterStatusCodeOk := c.Bool("filter-status-code-ok")
	jsonOutput := c.Bool("json-output")
	subsetSelection := c.Bool("subset-selection")
	sizeConstraint := c.Int("size-constraint")
	alpha := c.Float64("alpha")
	r := c.Float64("r")
	scoreThreshold := c.Float64("score-threshold")
	durationDay := c.Int64("duration-day")
	listName := c.String("listname")
	rule, ok := listName2Rule[listName]
	if ok == false {
		return cli.NewExitError("No matched rule", 1)
	}

	cache, err := cache.NewCache()
	if err != nil {
		return err
	}
	defer cache.Close()

	conn, err := db.CreateDBConnection()
	if err != nil {
		return err
	}
	defer conn.Close()

	examples, err := db.ReadLabeledExamples(conn, 10000)
	if err != nil {
		return err
	}

	cache.AttachMetaData(examples)
	if filterStatusCodeOk {
		examples = util.FilterStatusCodeOkExamples(examples)
	}
	model := classifier.NewBinaryClassifier(examples)

	targetExamples, err := db.ReadRecentExamples(conn, time.Now().Add(-time.Duration(24*durationDay)*time.Hour))
	if err != nil {
		return err
	}
	targetExamples = util.RemoveNegativeExamples(targetExamples)
	cache.AttachMetaData(targetExamples)
	if filterStatusCodeOk {
		targetExamples = util.FilterStatusCodeOkExamples(targetExamples)
	}

	result := example.Examples{}
	for _, e := range targetExamples {
		if !rule.MatchString(e.FinalUrl) {
			continue
		}
		e.Score = model.PredictScore(e.Fv)
		e.Title = strings.Replace(e.Title, "\n", " ", -1)
		if e.Score > scoreThreshold {
			result = append(result, e)
		}
	}

	if subsetSelection {
		result = submodular.SelectSubExamplesBySubModular(result, sizeConstraint, alpha, r)
	}

	err = cache.AddExamplesToList(listName, result)
	if err != nil {
		return err
	}

	for _, e := range result {
		if jsonOutput {
			b, err := json.Marshal(e)
			if err != nil {
				return err
			}
			fmt.Println(string(b))
		} else {
			fmt.Println(fmt.Sprintf("%0.03f\t%s", e.Score, e.Url))
		}
	}

	return nil
}

var CommandApply = cli.Command{
	Name:  "apply",
	Usage: "Apply classifier to unlabeled examples",
	Description: `
Apply classifier to unlabeled examples, and print a pair of score and url.
`,
	Action: doApply,
	Flags: []cli.Flag{
		cli.BoolFlag{Name: "filter-status-code-ok", Usage: "Use only examples with status code = 200"},
		cli.BoolFlag{Name: "json-output", Usage: "Make output with json format or not (tsv format)."},
		cli.BoolFlag{Name: "subset-selection", Usage: "Use subset selection algorithm (maximizing submodular function) to filter entries"},
		cli.Int64Flag{Name: "size-constraint", Value: 10, Usage: "Budget constraint. Max number of entries to be contained"},
		cli.Float64Flag{Name: "alpha", Value: 1.0},
		cli.Float64Flag{Name: "r", Value: 1.0, Usage: "Scaling factor for number of words"},
		cli.Float64Flag{Name: "score-threshold", Value: 0.0},
		cli.StringFlag{Name: "listname", Usage: "List name for cache"},
		cli.Int64Flag{Name: "duration-day", Usage: "Time span for fetching prediction target", Value: 2},
	},
}
<|endoftext|>


<commit_before>/*
  multimap.go.  multithreaded directoy mapping.  Was pmap but that conflicted with a system command.
  22 Oct 2018 -- Started coding this based on MichaelTJones' multithreaded code.
*/
package main

import (
	"bufio"
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"sync"
	"time"
	"timlibg"

	"github.com/MichaelTJones/walk"
)

const LastAlteredDate = "Oct 22, 2018"

var duration = flag.String("d", "", "find files modified within DURATION")
var format = flag.String("f", "2006-01-02 03:04:05", "time format")
var instant = flag.String("t", "", "find files modified since TIME")
var quiet = flag.Bool("q", false, "do not print filenames")
var verbose = flag.Bool("v", false, "print summary statistics")
var help = flag.Bool("h", false, "print help message")

type directory struct {
	name     string
	subtotal int64
}

type item struct {
	name string
	size int64
}

type dirslice []directory

func (ds dirslice) Less(i, j int) bool {
	return ds[i].subtotal > ds[j].subtotal // I want a reverse sort, largest first
}

func (ds dirslice) Swap(i, j int) {
	ds[i], ds[j] = ds[j], ds[i]
}

func (ds dirslice) Len() int {
	return len(ds)
}

func main() {
	fmt.Println("multimap is a multithreaded directory mapping written in Go.  Last Altered", LastAlteredDate)

	var GrandTotalSize, TotalOfFiles int64
	var startDirectory string
	var dirList dirslice
	var err error
	now := time.Now()

	fmt.Println()

	if len(os.Args) < 2 {
		startDirectory, err = os.Getwd()
		if err != nil {
			log.Fatalln(" error from Getwd is", err)
		}
	} else {
		startDirectory = os.Args[1]
	}
	start, err := os.Stat(startDirectory)
	if err != nil || !start.IsDir() {
		fmt.Println(" usage: dirmap <directoryname>")
		os.Exit(1)
	}

	dirList = make(dirslice, 0, 1024)
	DirMap := make(map[string]int64, 1024)

	// goroutine to collect items
	done := make(chan bool)
	results := make(chan item, 1024)
	var lock sync.Mutex
	go func() {
		for r := range results {
			lock.Lock()
			DirMap[r.name] += r.size
			lock.Unlock()
		}
		done <- true
	}()

	// parallel walker to find dirs
	var tFiles, tBytes int64 // total files and bytes
	sizeVisitor := func(path string, info os.FileInfo, err error) error {
		var d item
		if err == nil {
			lock.Lock()
			tFiles += 1
			tBytes += info.Size()
			lock.Unlock()

			if info.IsDir() {
				d.name = path
				d.size = info.Size()
			} else {
				d.name = filepath.Dir(path)
				d.size = info.Size()
			}
			results <- d
		} else {
			fmt.Printf(" Error from walk.  Grand total size is %d in %d number of files, error is %v. \n ",
				GrandTotalSize, TotalOfFiles, err)
		}
		return nil
	}
	walk.Walk(startDirectory, sizeVisitor)

	// wait for traversal results and prepare for output.
	close(results) // no more results
	<-done         // wait for final results

	s2 := ""
	var i int64 = tBytes
	switch {
	case tBytes > 1e12: // 1 trillion, or TB
		i = tBytes / 1e12       // I'm forcing an integer division.
		if tBytes%1e12 > 5e11 { // rounding up
			i++
		}
		s2 = fmt.Sprintf("%d TB", i)
	case tBytes > 1e9: // 1 billion, or GB
		i = tBytes / 1e9
		if tBytes%1e9 > 5e8 { // rounding up
			i++
		}
		s2 = fmt.Sprintf("%d GB", i)
	case tBytes > 1e6: // 1 million, or MB
		i = tBytes / 1e6
		if tBytes%1e6 > 5e5 {
			i++
		}
		s2 = fmt.Sprintf("%d MB", i)
	case tBytes > 1000: // KB
		i = tBytes / 1000
		if tBytes%1000 > 500 {
			i++
		}
		s2 = fmt.Sprintf("%d KB", i)
	default:
		s2 = fmt.Sprintf("%d", i)
	}

	GrandTotalString := strconv.FormatInt(tBytes, 10)
	GrandTotalString = AddCommas(GrandTotalString)

	// Construct output filename
	datestr := MakeDateStr()
	outfilename := "dirmap_" + filepath.Base(startDirectory) + datestr + ".txt"
	outfile, err := os.Create(outfilename)
	defer outfile.Close()
	var bufoutfile = bufio.NewWriter(outfile)
	defer bufoutfile.Flush()

	//	outputfile := bufio.NewWriter(outfile)  these may duplicate the "expert" code below.
	//	defer outputfile.Flush()
	if err != nil {
		fmt.Println(" Cannot open outputfile ", outfilename, " with error ", err)
		// I'm going to assume this branch does not occur in the code below.  Else I would need a
		// stop flag of some kind to write to screen.
	}

	// Construct output map
	for n, m := range DirMap { // n is name as a string, m is map as a directory subtotal
		d := directory{} // this is a structured constant
		d.name = n
		d.subtotal = m
		dirList = append(dirList, d)
	}
	sort.Sort(dirList)

	𝛥t := float64(time.Since(now)) / 1e9

	s0 := fmt.Sprintf("start dir is %s, found %d files in this tree.  GrandTotal is %s, or %s, and number of directories is %d, took %.4g s to generate.\n",
		startDirectory, tFiles, GrandTotalString, s2, len(DirMap), 𝛥t)
	fmt.Println(s0)
	_, err = bufoutfile.WriteString(s0)
	_, err = bufoutfile.WriteRune('\n')
	if err != nil {
		fmt.Println(" error from writing bufoutfile is", err)
		os.Exit(1)
	}

	for _, d := range dirList {
		var str = strconv.FormatInt(d.subtotal, 10)
		str = AddCommas(str)
		s5 := fmt.Sprintf("%s size is %s\n", d.name, str)
		_, err := bufoutfile.WriteString(s5)
		if err != nil {
			fmt.Println(" error from writing to bufoutfile while writing dirList.  Error is", err)
			os.Exit(1)
		}
	}
	fmt.Println(" List of", len(dirList), " (sub)directories written to", outfilename)
}

//-------------------------------------------------------------------- InsertByteSlice
func InsertIntoByteSlice(slice, insertion []byte, index int) []byte {
	return append(slice[:index], append(insertion, slice[index:]...)...)
}

//---------------------------------------------------------------------- AddCommas
func AddCommas(instr string) string {
	var Comma []byte = []byte{','}

	BS := make([]byte, 0, 15)
	BS = append(BS, instr...)

	i := len(BS)

	for NumberOfCommas := i / 3; (NumberOfCommas > 0) && (i > 3); NumberOfCommas-- {
		i -= 3
		BS = InsertIntoByteSlice(BS, Comma, i)
	}
	return string(BS)
} // AddCommas

//------------------------------------------------------------------- min
func min(i, j int) int {
	if i < j {
		return i
	} else {
		return j
	}
} // min

// ------------------------------------------- MakeDateStr ---------------------------------------------
func MakeDateStr() (datestr string) {

	const DateSepChar = "-"

	m, d, y := timlibg.TIME2MDY()
	timenow := timlibg.GetDateTime()

	MSTR := strconv.Itoa(m)
	DSTR := strconv.Itoa(d)
	YSTR := strconv.Itoa(y)
	Hr := strconv.Itoa(timenow.Hours)
	Min := strconv.Itoa(timenow.Minutes)
	Sec := strconv.Itoa(timenow.Seconds)

	datestr = "_" + MSTR + DateSepChar + DSTR + DateSepChar + YSTR + "_" + Hr + DateSepChar + Min + DateSepChar +
		Sec + "__" + timenow.DayOfWeekStr
	return datestr
} // MakeDateStr
<commit_msg>modified:   multimap/multimap.go 10/23/2018 06:58:33 AM<commit_after>/*
  multimap.go.  multithreaded directoy mapping.  Was pmap but that conflicted with a system command.
  22 Oct 2018 -- Started coding this based on MichaelTJones' multithreaded code.
*/
package main

import (
	"bufio"
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"sync"
	"time"
	"timlibg"

	"github.com/MichaelTJones/walk"
)

const LastAlteredDate = "Oct 23, 2018"

var duration = flag.String("d", "", "find files modified within DURATION")
var format = flag.String("f", "2006-01-02 03:04:05", "time format")
var instant = flag.String("t", "", "find files modified since TIME")
var quiet = flag.Bool("q", false, "do not print filenames")
var verbose = flag.Bool("v", false, "print summary statistics")
var help = flag.Bool("h", false, "print help message")

type directory struct {
	name     string
	subtotal int64
}

type item struct {
	name string
	size int64
}

type dirslice []directory

func (ds dirslice) Less(i, j int) bool {
	return ds[i].subtotal > ds[j].subtotal // I want a reverse sort, largest first
}

func (ds dirslice) Swap(i, j int) {
	ds[i], ds[j] = ds[j], ds[i]
}

func (ds dirslice) Len() int {
	return len(ds)
}

func main() {
	fmt.Println("multimap is a multithreaded directory mapping written in Go.  Last Altered", LastAlteredDate)

	var GrandTotalSize, TotalOfFiles int64
	var startDirectory string
	var dirList dirslice
	var err error
	now := time.Now()

	fmt.Println()

	if len(os.Args) < 2 {
		startDirectory, err = os.Getwd()
		if err != nil {
			log.Fatalln(" error from Getwd is", err)
		}
	} else {
		startDirectory = os.Args[1]
	}
	start, err := os.Stat(startDirectory)
	if err != nil || !start.IsDir() {
		fmt.Println(" usage: dirmap <directoryname>")
		os.Exit(1)
	}

	dirList = make(dirslice, 0, 1024)
	DirMap := make(map[string]int64, 1024)

	// goroutine to collect items
	done := make(chan bool)
	results := make(chan item, 1024)
	var lock sync.Mutex
	go func() {
		for r := range results {
			lock.Lock()
			DirMap[r.name] += r.size
			lock.Unlock()
		}
		done <- true
	}()

	// parallel walker to find dirs
	var tFiles, tBytes int64 // total files and bytes
	sizeVisitor := func(path string, info os.FileInfo, err error) error {
		var d item
		if err == nil {
			lock.Lock()
			tFiles += 1
			tBytes += info.Size()
			lock.Unlock()

			if info.IsDir() {
				d.name = path
				d.size = info.Size()
			} else {
				d.name = filepath.Dir(path)
				d.size = info.Size()
			}
			results <- d
		} else {
			fmt.Printf(" Error from walk.  Grand total size is %d in %d number of files, error is %v. \n ",
				GrandTotalSize, TotalOfFiles, err)
		}
		return nil
	}
	walk.Walk(startDirectory, sizeVisitor)

	// wait for traversal results and prepare for output.
	close(results) // no more results
	<-done         // wait for final results

	s2 := ""
	var i int64 = tBytes
	switch {
	case tBytes > 1e12: // 1 trillion, or TB
		i = tBytes / 1e12       // I'm forcing an integer division.
		if tBytes%1e12 > 5e11 { // rounding up
			i++
		}
		s2 = fmt.Sprintf("%d TB", i)
	case tBytes > 1e9: // 1 billion, or GB
		i = tBytes / 1e9
		if tBytes%1e9 > 5e8 { // rounding up
			i++
		}
		s2 = fmt.Sprintf("%d GB", i)
	case tBytes > 1e6: // 1 million, or MB
		i = tBytes / 1e6
		if tBytes%1e6 > 5e5 {
			i++
		}
		s2 = fmt.Sprintf("%d MB", i)
	case tBytes > 1000: // KB
		i = tBytes / 1000
		if tBytes%1000 > 500 {
			i++
		}
		s2 = fmt.Sprintf("%d KB", i)
	default:
		s2 = fmt.Sprintf("%d", i)
	}

	GrandTotalString := strconv.FormatInt(tBytes, 10)
	GrandTotalString = AddCommas(GrandTotalString)

	// Construct output filename
	datestr := MakeDateStr()
	outfilename := "multimap_" + filepath.Base(startDirectory) + datestr + ".txt"
	outfile, err := os.Create(outfilename)
	defer outfile.Close()
	var bufoutfile = bufio.NewWriter(outfile)
	defer bufoutfile.Flush()

	//	outputfile := bufio.NewWriter(outfile)  these may duplicate the "expert" code below.
	//	defer outputfile.Flush()
	if err != nil {
		fmt.Println(" Cannot open outputfile ", outfilename, " with error ", err)
		// I'm going to assume this branch does not occur in the code below.  Else I would need a
		// stop flag of some kind to write to screen.
	}

	// Construct output map
	for n, m := range DirMap { // n is name as a string, m is map as a directory subtotal
		d := directory{} // this is a structured constant
		d.name = n
		d.subtotal = m
		dirList = append(dirList, d)
	}
	sort.Sort(dirList)

	𝛥t := float64(time.Since(now)) / 1e9

	s0 := fmt.Sprintf("start dir is %s, found %d files in this tree.  GrandTotal is %s, or %s, and number of directories is %d, took %.4g s to generate.\n",
		startDirectory, tFiles, GrandTotalString, s2, len(DirMap), 𝛥t)
	fmt.Println(s0)
	_, err = bufoutfile.WriteString(s0)
	_, err = bufoutfile.WriteRune('\n')
	if err != nil {
		fmt.Println(" error from writing bufoutfile is", err)
		os.Exit(1)
	}

	for _, d := range dirList {
		var str = strconv.FormatInt(d.subtotal, 10)
		str = AddCommas(str)
		s5 := fmt.Sprintf("%s size is %s\n", d.name, str)
		_, err := bufoutfile.WriteString(s5)
		if err != nil {
			fmt.Println(" error from writing to bufoutfile while writing dirList.  Error is", err)
			os.Exit(1)
		}
	}
	fmt.Println(" List of", len(dirList), " (sub)directories written to", outfilename)
}

//-------------------------------------------------------------------- InsertByteSlice
func InsertIntoByteSlice(slice, insertion []byte, index int) []byte {
	return append(slice[:index], append(insertion, slice[index:]...)...)
}

//---------------------------------------------------------------------- AddCommas
func AddCommas(instr string) string {
	var Comma []byte = []byte{','}

	BS := make([]byte, 0, 15)
	BS = append(BS, instr...)

	i := len(BS)

	for NumberOfCommas := i / 3; (NumberOfCommas > 0) && (i > 3); NumberOfCommas-- {
		i -= 3
		BS = InsertIntoByteSlice(BS, Comma, i)
	}
	return string(BS)
} // AddCommas

//------------------------------------------------------------------- min
func min(i, j int) int {
	if i < j {
		return i
	} else {
		return j
	}
} // min

// ------------------------------------------- MakeDateStr ---------------------------------------------
func MakeDateStr() (datestr string) {

	const DateSepChar = "-"

	m, d, y := timlibg.TIME2MDY()
	timenow := timlibg.GetDateTime()

	MSTR := strconv.Itoa(m)
	DSTR := strconv.Itoa(d)
	YSTR := strconv.Itoa(y)
	Hr := strconv.Itoa(timenow.Hours)
	Min := strconv.Itoa(timenow.Minutes)
	Sec := strconv.Itoa(timenow.Seconds)

	datestr = "_" + MSTR + DateSepChar + DSTR + DateSepChar + YSTR + "_" + Hr + DateSepChar + Min + DateSepChar +
		Sec + "__" + timenow.DayOfWeekStr
	return datestr
} // MakeDateStr
<|endoftext|>


<commit_before>package models

import (
    "github.com/orc/db"
    "log"
    "strconv"
)

type GroupsModel struct {
    Entity
}

type Groups struct {
    Id    int    `name:"id" type:"int" null:"NOT NULL" extra:"PRIMARY"`
    Name  string `name:"name" type:"text" null:"NOT NULL" extra:"UNIQUE"`
    Owner int    `name:"face_id" type:"int" null:"NOT NULL" extra:"REFERENCES" refTable:"faces" refField:"id" refFieldShow:"id"`
}

func (c *ModelManager) Groups() *GroupsModel {
    model := new(GroupsModel)

    model.TableName = "groups"
    model.Caption = "Группы"

    model.Columns = []string{"id", "name", "face_id"}
    model.ColNames = []string{"ID", "Название", "Владелец"}

    model.Fields = new(Groups)
    model.WherePart = make(map[string]interface{}, 0)
    model.Condition = AND
    model.OrderBy = "id"
    model.Limit = "ALL"
    model.Offset = 0

    model.Sub = true
    model.SubTable = []string{"persons"}
    model.SubField = "group_id"

    return model
}

func (this *GroupsModel) Update(userId, rowId int, params map[string]interface{}) {
    faceId := -1
    query := `SELECT groups.face_id FROM groups
        INNER JOIN faces ON faces.id = groups.face_id
        INNER JOIN users ON users.id = faces.user_id
        WHERE users.id = $1 AND groups.id = $2;`
    err := db.QueryRow(query, []interface{}{userId, rowId}).Scan(&faceId)

    if err != nil {
        log.Println(err.Error())
        return

    } else if faceId == -1 {
        log.Println("Нет прав редактировать эту группу")
        return
    }

    params["face_id"] = faceId
    this.LoadModelData(params)
    this.LoadWherePart(map[string]interface{}{"id": rowId})
    db.QueryUpdate_(this).Scan()
}

func (this *GroupsModel) Add(userId int, params map[string]interface{}) {
    var faceId int
    query := `SELECT faces.id
        FROM registrations
        INNER JOIN faces ON faces.id = registrations.face_id
        INNER JOIN events ON events.id = registrations.event_id
        INNER JOIN users ON faces.user_id = users.id
        WHERE users.id = $1 AND events.id = $2;`
    db.QueryRow(query, []interface{}{userId, 1}).Scan(&faceId)
    params["face_id"] = faceId
    this.LoadModelData(params)
    db.QueryInsert_(this, "").Scan()
}

func (this *GroupsModel) Select(fields []string, filters map[string]interface{}, limit, offset int, sord, sidx string) (result []interface{}) {
    if len(fields) == 0 {
        return nil
    }

    query := `SELECT `

    for _, field := range fields {
        switch field {
        case "id":
            query += "groups.id, "
            break
        case "name":
            query += "groups.name as group_name, "
            break
        case "face_id":
            query += "array_to_string(array_agg(param_values.value), ' ') as face_name, "
            break
        }
    }

    query = query[:len(query)-2]

    query += ` FROM reg_param_vals
        INNER JOIN registrations ON registrations.id = reg_param_vals.reg_id
        INNER JOIN faces ON faces.id = registrations.face_id
        INNER JOIN events ON events.id = registrations.event_id
        INNER JOIN param_values ON param_values.id = reg_param_vals.param_val_id
        INNER JOIN params ON params.id = param_values.param_id
        INNER JOIN groups ON groups.face_id = faces.id`

    where, params, _ := this.Where(filters, 1)

    if where != "" {
        query += ` WHERE ` + where + ` AND params.id in (5, 6, 7) AND events.id = 1 GROUP BY groups.id`
    } else {
        query += ` WHERE params.id in (5, 6, 7) AND events.id = 1 GROUP BY groups.id`
    }

    if sidx != "" {
        query += ` ORDER BY groups.`+sidx
    }

    query += ` `+ sord

    if limit != -1 {
        params = append(params, limit)
        query += ` LIMIT $`+strconv.Itoa(len(params))
    }

    if offset != -1 {
        params = append(params, offset)
        query += ` OFFSET $`+strconv.Itoa(len(params))
    }

    query += `;`

    return db.Query(query, params)
}

func (this *GroupsModel) GetColModel() []map[string]interface{} {
    query := `SELECT array_to_string(
        array(SELECT faces.id || ':' || array_to_string(array_agg(param_values.value), ' ')
        FROM reg_param_vals
        INNER JOIN registrations ON registrations.id = reg_param_vals.reg_id
        INNER JOIN faces ON faces.id = registrations.face_id
        INNER JOIN events ON events.id = registrations.event_id
        INNER JOIN param_values ON param_values.id = reg_param_vals.param_val_id
        INNER JOIN params ON params.id = param_values.param_id
        WHERE params.id in (5, 6, 7) GROUP BY faces.id ORDER BY faces.id), ';') as name;`

    faces := db.Query(query, nil)[0].(map[string]interface{})["name"].(string)

    return []map[string]interface{} {
        0: map[string]interface{} {
            "index": "id",
            "name": "id",
            "editable": false,
        },
        1: map[string]interface{} {
            "index": "name",
            "name": "name",
            "editable": true,
            "editrules": map[string]interface{}{"required": true},
        },
        2: map[string]interface{} {
            "index": "face_id",
            "name": "face_id",
            "editable": true,
            "formatter": "select",
            "edittype": "select",
            "stype": "select",
            "search": true,
            "editrules": map[string]interface{}{"required": true},
            "editoptions": map[string]string{"value": faces},
            "searchoptions": map[string]string{"value": ":Все;"+faces},
        },
    }
}
<commit_msg>groups: add Delete method<commit_after>package models

import (
    "github.com/orc/db"
    "log"
    "strconv"
)

type GroupsModel struct {
    Entity
}

type Groups struct {
    Id    int    `name:"id" type:"int" null:"NOT NULL" extra:"PRIMARY"`
    Name  string `name:"name" type:"text" null:"NOT NULL" extra:"UNIQUE"`
    Owner int    `name:"face_id" type:"int" null:"NOT NULL" extra:"REFERENCES" refTable:"faces" refField:"id" refFieldShow:"id"`
}

func (c *ModelManager) Groups() *GroupsModel {
    model := new(GroupsModel)

    model.TableName = "groups"
    model.Caption = "Группы"

    model.Columns = []string{"id", "name", "face_id"}
    model.ColNames = []string{"ID", "Название", "Владелец"}

    model.Fields = new(Groups)
    model.WherePart = make(map[string]interface{}, 0)
    model.Condition = AND
    model.OrderBy = "id"
    model.Limit = "ALL"
    model.Offset = 0

    model.Sub = true
    model.SubTable = []string{"persons"}
    model.SubField = "group_id"

    return model
}

func (this *GroupsModel) Update(userId, rowId int, params map[string]interface{}) {
    faceId := -1
    query := `SELECT groups.face_id FROM groups
        INNER JOIN faces ON faces.id = groups.face_id
        INNER JOIN users ON users.id = faces.user_id
        WHERE users.id = $1 AND groups.id = $2;`
    err := db.QueryRow(query, []interface{}{userId, rowId}).Scan(&faceId)

    if err != nil {
        log.Println(err.Error())
        return

    } else if faceId == -1 {
        log.Println("Нет прав редактировать эту группу")
        return
    }

    params["face_id"] = faceId
    this.LoadModelData(params)
    this.LoadWherePart(map[string]interface{}{"id": rowId})
    db.QueryUpdate_(this).Scan()
}

func (this *GroupsModel) Add(userId int, params map[string]interface{}) {
    var faceId int
    query := `SELECT faces.id
        FROM registrations
        INNER JOIN faces ON faces.id = registrations.face_id
        INNER JOIN events ON events.id = registrations.event_id
        INNER JOIN users ON faces.user_id = users.id
        WHERE users.id = $1 AND events.id = $2;`
    db.QueryRow(query, []interface{}{userId, 1}).Scan(&faceId)
    params["face_id"] = faceId
    this.LoadModelData(params)
    db.QueryInsert_(this, "").Scan()
}

func (this *GroupsModel) Delete(id int) {
    query := `DELETE
        FROM persons
        WHERE persons.group_id = $1;`
    db.Query(query, []interface{}{id})

    query = `DELETE FROM groups WHERE id = $1;`
    db.Query(query, []interface{}{id})
}

func (this *GroupsModel) Select(fields []string, filters map[string]interface{}, limit, offset int, sord, sidx string) (result []interface{}) {
    if len(fields) == 0 {
        return nil
    }

    query := `SELECT `

    for _, field := range fields {
        switch field {
        case "id":
            query += "groups.id, "
            break
        case "name":
            query += "groups.name as group_name, "
            break
        case "face_id":
            query += "array_to_string(array_agg(param_values.value), ' ') as face_name, "
            break
        }
    }

    query = query[:len(query)-2]

    query += ` FROM reg_param_vals
        INNER JOIN registrations ON registrations.id = reg_param_vals.reg_id
        INNER JOIN faces ON faces.id = registrations.face_id
        INNER JOIN events ON events.id = registrations.event_id
        INNER JOIN param_values ON param_values.id = reg_param_vals.param_val_id
        INNER JOIN params ON params.id = param_values.param_id
        INNER JOIN groups ON groups.face_id = faces.id`

    where, params, _ := this.Where(filters, 1)

    if where != "" {
        query += ` WHERE ` + where + ` AND params.id in (5, 6, 7) AND events.id = 1 GROUP BY groups.id`
    } else {
        query += ` WHERE params.id in (5, 6, 7) AND events.id = 1 GROUP BY groups.id`
    }

    if sidx != "" {
        query += ` ORDER BY groups.`+sidx
    }

    query += ` `+ sord

    if limit != -1 {
        params = append(params, limit)
        query += ` LIMIT $`+strconv.Itoa(len(params))
    }

    if offset != -1 {
        params = append(params, offset)
        query += ` OFFSET $`+strconv.Itoa(len(params))
    }

    query += `;`

    return db.Query(query, params)
}

func (this *GroupsModel) GetColModel() []map[string]interface{} {
    query := `SELECT array_to_string(
        array(SELECT faces.id || ':' || array_to_string(array_agg(param_values.value), ' ')
        FROM reg_param_vals
        INNER JOIN registrations ON registrations.id = reg_param_vals.reg_id
        INNER JOIN faces ON faces.id = registrations.face_id
        INNER JOIN events ON events.id = registrations.event_id
        INNER JOIN param_values ON param_values.id = reg_param_vals.param_val_id
        INNER JOIN params ON params.id = param_values.param_id
        WHERE params.id in (5, 6, 7) GROUP BY faces.id ORDER BY faces.id), ';') as name;`

    faces := db.Query(query, nil)[0].(map[string]interface{})["name"].(string)

    return []map[string]interface{} {
        0: map[string]interface{} {
            "index": "id",
            "name": "id",
            "editable": false,
        },
        1: map[string]interface{} {
            "index": "name",
            "name": "name",
            "editable": true,
            "editrules": map[string]interface{}{"required": true},
        },
        2: map[string]interface{} {
            "index": "face_id",
            "name": "face_id",
            "editable": true,
            "formatter": "select",
            "edittype": "select",
            "stype": "select",
            "search": true,
            "editrules": map[string]interface{}{"required": true},
            "editoptions": map[string]string{"value": faces},
            "searchoptions": map[string]string{"value": ":Все;"+faces},
        },
    }
}
<|endoftext|>


<commit_before><commit_msg>add concurrency protection<commit_after><|endoftext|>


<commit_before><commit_msg>Report required and available slots<commit_after><|endoftext|>


<commit_before>// Package neptulon is a socket framework with middleware support.
package neptulon

import (
	"log"
	"sync"
)

// Neptulon framework entry point.
type Neptulon struct {
	debug      bool
	err        error
	errMutex   sync.RWMutex
	listener   *Listener
	middleware []func(conn *Conn, session *Session, msg []byte)
	conns      map[string]*Conn
}

// New creates and returns a new Neptulon app. This is the default TLS constructor.
// Debug mode dumps raw TCP data to stderr (log.Println() default).
func New(cert, privKey []byte, laddr string, debug bool) (*Neptulon, error) {
	l, err := Listen(cert, privKey, laddr, debug)
	if err != nil {
		return nil, err
	}

	return &Neptulon{
		debug:    debug,
		listener: l,
	}, nil
}

// Middleware registers a new middleware to handle incoming messages.
func (n *Neptulon) Middleware(middleware func(conn *Conn, session *Session, msg []byte)) {
	n.middleware = append(n.middleware, middleware)
}

// Run starts accepting connections on the internal listener and handles connections with registered middleware.
// This function blocks and never returns, unless there was an error while accepting a new connection or the listner was closed.
func (n *Neptulon) Run() error {
	err := n.listener.Accept(handleConn(n), handleMsg(n), handleDisconn(n))
	if err != nil && n.debug {
		log.Fatalln("Listener returned an error while closing:", err)
	}

	n.errMutex.Lock()
	n.err = err
	n.errMutex.Unlock()

	return err
}

// Stop stops a server instance.
func (n *Neptulon) Stop() error {
	err := n.listener.Close()

	// close all active connections discarding any read/writes that is going on currently
	// this is not a problem as we always require an ACK but it will also mean that message deliveries will be at-least-once; to-and-from the server
	for _, conn := range n.conns {
		err := conn.Close()
		if err != nil {
			return err
		}
	}

	n.errMutex.RLock()
	if n.err != nil {
		return n.err
	}
	n.errMutex.RUnlock()
	return err
}

// handleConn handles client connected event.
func handleConn(n *Neptulon) func(conn *Conn, session *Session) {
	return func(conn *Conn, session *Session) {
		n.conns[session.id] = conn
	}
}

// handleMsg handles incoming client messages.
func handleMsg(n *Neptulon) func(conn *Conn, session *Session, msg []byte) {
	return func(conn *Conn, session *Session, msg []byte) {
		for _, m := range n.middleware {
			m(conn, session, msg)
		}
	}
}

// handleDisconn handles client disconnection.
func handleDisconn(n *Neptulon) func(conn *Conn, session *Session) {
	return func(conn *Conn, session *Session) {
		delete(n.conns, session.id)
	}
}
<commit_msg>remove clutter<commit_after>// Package neptulon is a socket framework with middleware support.
package neptulon

import (
	"log"
	"sync"
)

// Neptulon framework entry point.
type Neptulon struct {
	debug      bool
	err        error
	errMutex   sync.RWMutex
	listener   *Listener
	middleware []func(conn *Conn, session *Session, msg []byte)
	conns      map[string]*Conn
}

// New creates and returns a new Neptulon app. This is the default TLS constructor.
// Debug mode dumps raw TCP data to stderr (log.Println() default).
func New(cert, privKey []byte, laddr string, debug bool) (*Neptulon, error) {
	l, err := Listen(cert, privKey, laddr, debug)
	if err != nil {
		return nil, err
	}

	return &Neptulon{
		debug:    debug,
		listener: l,
	}, nil
}

// Middleware registers a new middleware to handle incoming messages.
func (n *Neptulon) Middleware(middleware func(conn *Conn, session *Session, msg []byte)) {
	n.middleware = append(n.middleware, middleware)
}

// Run starts accepting connections on the internal listener and handles connections with registered middleware.
// This function blocks and never returns, unless there was an error while accepting a new connection or the listner was closed.
func (n *Neptulon) Run() error {
	err := n.listener.Accept(handleConn(n), handleMsg(n), handleDisconn(n))
	if err != nil && n.debug {
		log.Fatalln("Listener returned an error while closing:", err)
	}

	n.errMutex.Lock()
	n.err = err
	n.errMutex.Unlock()

	return err
}

// Stop stops a server instance.
func (n *Neptulon) Stop() error {
	err := n.listener.Close()

	// close all active connections discarding any read/writes that is going on currently
	// this is not a problem as we always require an ACK but it will also mean that message deliveries will be at-least-once; to-and-from the server
	for _, conn := range n.conns {
		err := conn.Close()
		if err != nil {
			return err
		}
	}

	n.errMutex.RLock()
	if n.err != nil {
		return n.err
	}
	n.errMutex.RUnlock()
	return err
}

func handleConn(n *Neptulon) func(conn *Conn, session *Session) {
	return func(conn *Conn, session *Session) {
		n.conns[session.id] = conn
	}
}

func handleMsg(n *Neptulon) func(conn *Conn, session *Session, msg []byte) {
	return func(conn *Conn, session *Session, msg []byte) {
		for _, m := range n.middleware {
			m(conn, session, msg)
		}
	}
}

func handleDisconn(n *Neptulon) func(conn *Conn, session *Session) {
	return func(conn *Conn, session *Session) {
		delete(n.conns, session.id)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
	"text/template"
)

// Go Template Function Map here

var genTimeFunctions = template.FuncMap{
	// simple additon function useful for counters in loops
	"add": func(a int, b int) int {
		Log(fmt.Sprintln("Calling Template Function [Add] with arguments:", a, b), level.debug)
		return a + b
	},

	// strip function for removing characters from text
	"strip": func(s string, rmv string) string {
		Log(fmt.Sprintln("Calling Template Function [Strip] with arguments:", s, rmv), level.debug)
		return strings.Replace(s, rmv, "", -1)
	},

	// file function for reading text from a given file under the files folder
	"file": func(filename string) (string, error) {

		Log(fmt.Sprintln("Calling Template Function [File] with arguments:", filename), level.debug)
		p := job.tplFiles[0]
		f := filepath.Join(filepath.Dir(p), "..", "files", filename)
		fmt.Println(f)
		b, err := ioutil.ReadFile(f)
		if err != nil {
			return "", err
		}
		return string(b), nil
	},

	// Get get does an HTTP Get request of the given url and returns the output string
	"GET": func(url string) (string, error) {
		Log(fmt.Sprintln("Calling Template Function [GET] with arguments:", url), level.debug)
		resp, err := Get(url)
		if err != nil {
			return "", err
		}

		return resp, nil
	},

	// S3Read reads content of file from s3 and returns string contents
	"s3_read": func(url string) (string, error) {
		Log(fmt.Sprintln("Calling Template Function [S3Read] with arguments:", url), level.debug)
		resp, err := S3Read(url)
		if err != nil {
			return "", err
		}
		return resp, nil
	},

	// invoke - invokes a lambda function
	"invoke": func(name string, payload string) (string, error) {
		f := function{name: name}
		if payload != "" {
			f.payload = []byte(payload)
		}

		sess, err := awsSession()
		if err != nil {
			return "", err
		}

		if err := f.Invoke(sess); err != nil {
			return "", err
		}

		return f.response, nil
	},
}

var deployTimeFunctions = template.FuncMap{
	// Fetching stackoutputs
	"stack_output": func(target string) (string, error) {
		Log(fmt.Sprintf("Deploy-Time function resolving: %s", target), level.debug)
		req := strings.Split(target, "::")
		sess, err := awsSession()
		if err != nil {
			return "", nil
		}

		stkname := strings.Join([]string{project, req[0]}, "-")
		outputs, err := StackOutputs(stkname, sess)

		if err != nil {
			return "", err
		}

		for _, i := range outputs.Stacks {
			for _, o := range i.Outputs {
				if *o.OutputKey == req[1] {
					return *o.OutputValue, nil
				}
			}
		}

		return "", fmt.Errorf("Stack Output Not found - Stack:%s | Output:%s", req[0], req[1])
	},

	"stack_output_ext": func(target string) (string, error) {
		Log(fmt.Sprintf("Deploy-Time function resolving: %s", target), level.debug)
		req := strings.Split(target, "::")
		sess, err := awsSession()
		if err != nil {
			return "", nil
		}

		outputs, err := StackOutputs(req[0], sess)

		if err != nil {
			return "", err
		}

		for _, i := range outputs.Stacks {
			for _, o := range i.Outputs {
				if *o.OutputKey == req[1] {
					return *o.OutputValue, nil
				}
			}
		}

		return "", fmt.Errorf("Stack Output Not found - Stack:%s | Output:%s", req[0], req[1])
	},

	// Get get does an HTTP Get request of the given url and returns the output string
	"GET": func(url string) (string, error) {
		Log(fmt.Sprintln("Calling Template Function [GET] with arguments:", url), level.debug)
		resp, err := Get(url)
		if err != nil {
			return "", err
		}

		return resp, nil
	},

	// S3Read reads content of file from s3 and returns string contents
	"s3_read": func(url string) (string, error) {
		Log(fmt.Sprintln("Calling Template Function [S3Read] with arguments:", url), level.debug)
		resp, err := S3Read(url)
		if err != nil {
			return "", err
		}
		return resp, nil
	},

	// invoke - invokes a lambda function
	"invoke": func(name string, payload string) (string, error) {
		f := function{name: name}
		if payload != "" {
			f.payload = []byte(payload)
		}

		sess, err := awsSession()
		if err != nil {
			return "", err
		}

		if err := f.Invoke(sess); err != nil {
			return "", err
		}

		return f.response, nil
	},
}
<commit_msg>Added Kms De/En-crpytion Deploy & Gen-Time functions. Restructured functions to reduce deplucate code. Added Error printing to functions<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
	"text/template"

	"encoding/base64"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/kms"
)

// Common Functions - Both Deploy/Gen

var kmsEncrypt = func(kid string, text string) (string, error) {
	sess, err := awsSession()
	if err != nil {
		Log(err.Error(), level.err)
		return "", err
	}

	svc := kms.New(sess)

	params := &kms.EncryptInput{
		KeyId:     aws.String(kid),
		Plaintext: []byte(text),
	}

	resp, err := svc.Encrypt(params)
	if err != nil {
		Log(err.Error(), level.err)
		return "", err
	}

	return base64.StdEncoding.EncodeToString(resp.CiphertextBlob), nil
}

var kmsDecrypt = func(cipher string) (string, error) {
	sess, err := awsSession()
	if err != nil {
		Log(err.Error(), level.err)
		return "", err
	}

	svc := kms.New(sess)

	ciph, err := base64.StdEncoding.DecodeString(cipher)
	if err != nil {
		Log(err.Error(), level.err)
		return "", err
	}

	params := &kms.DecryptInput{
		CiphertextBlob: []byte(ciph),
	}

	resp, err := svc.Decrypt(params)
	if err != nil {
		Log(err.Error(), level.err)
		return "", err
	}

	return string(resp.Plaintext), nil
}

var httpGet = func(url string) (interface{}, error) {
	Log(fmt.Sprintln("Calling Template Function [GET] with arguments:", url), level.debug)
	resp, err := Get(url)
	if err != nil {
		Log(err.Error(), level.err)
		return "", err
	}

	return resp, nil
}

var s3Read = func(url string) (string, error) {
	Log(fmt.Sprintln("Calling Template Function [S3Read] with arguments:", url), level.debug)
	resp, err := S3Read(url)
	if err != nil {
		Log(err.Error(), level.err)
		return "", err
	}
	return resp, nil
}

var lambdaInvoke = func(name string, payload string) (interface{}, error) {
	f := function{name: name}
	if payload != "" {
		f.payload = []byte(payload)
	}

	sess, err := awsSession()
	if err != nil {
		Log(err.Error(), level.err)
		return "", err
	}

	if err := f.Invoke(sess); err != nil {
		Log(err.Error(), level.err)
		return "", err
	}

	return f.response, nil
}

// template function maps

var genTimeFunctions = template.FuncMap{
	// simple additon function useful for counters in loops
	"add": func(a int, b int) int {
		Log(fmt.Sprintln("Calling Template Function [Add] with arguments:", a, b), level.debug)
		return a + b
	},

	// strip function for removing characters from text
	"strip": func(s string, rmv string) string {
		Log(fmt.Sprintln("Calling Template Function [Strip] with arguments:", s, rmv), level.debug)
		return strings.Replace(s, rmv, "", -1)
	},

	// file function for reading text from a given file under the files folder
	"file": func(filename string) (string, error) {

		Log(fmt.Sprintln("Calling Template Function [File] with arguments:", filename), level.debug)
		p := job.tplFiles[0]
		f := filepath.Join(filepath.Dir(p), "..", "files", filename)
		b, err := ioutil.ReadFile(f)
		if err != nil {
			Log(err.Error(), level.err)
			return "", err
		}
		return string(b), nil
	},

	// Get get does an HTTP Get request of the given url and returns the output string
	"GET": httpGet,

	// S3Read reads content of file from s3 and returns string contents
	"s3_read": s3Read,

	// invoke - invokes a lambda function
	"invoke": lambdaInvoke,

	// kms-encrypt - Encrypts PlainText using KMS key
	"kms_encrypt": kmsEncrypt,

	// kms-decrypt - Descrypts CipherText
	"kms_decrypt": kmsDecrypt,
}

var deployTimeFunctions = template.FuncMap{
	// Fetching stackoutputs
	"stack_output": func(target string) (string, error) {
		Log(fmt.Sprintf("Deploy-Time function resolving: %s", target), level.debug)
		req := strings.Split(target, "::")
		sess, err := awsSession()
		if err != nil {
			Log(err.Error(), level.err)
			return "", nil
		}

		stkname := strings.Join([]string{project, req[0]}, "-")
		outputs, err := StackOutputs(stkname, sess)

		if err != nil {
			return "", err
		}

		for _, i := range outputs.Stacks {
			for _, o := range i.Outputs {
				if *o.OutputKey == req[1] {
					return *o.OutputValue, nil
				}
			}
		}

		return "", fmt.Errorf("Stack Output Not found - Stack:%s | Output:%s", req[0], req[1])
	},

	"stack_output_ext": func(target string) (string, error) {
		Log(fmt.Sprintf("Deploy-Time function resolving: %s", target), level.debug)
		req := strings.Split(target, "::")
		sess, err := awsSession()
		if err != nil {
			Log(err.Error(), level.err)
			return "", nil
		}

		outputs, err := StackOutputs(req[0], sess)

		if err != nil {
			return "", err
		}

		for _, i := range outputs.Stacks {
			for _, o := range i.Outputs {
				if *o.OutputKey == req[1] {
					return *o.OutputValue, nil
				}
			}
		}

		return "", fmt.Errorf("Stack Output Not found - Stack:%s | Output:%s", req[0], req[1])
	},

	// Get get does an HTTP Get request of the given url and returns the output string
	"GET": httpGet,

	// S3Read reads content of file from s3 and returns string contents
	"s3_read": s3Read,

	// invoke - invokes a lambda function
	"invoke": lambdaInvoke,

	// kms-encrypt - Encrypts PlainText using KMS key
	"kms_encrypt": kmsEncrypt,

	// kms-decrypt - Descrypts CipherText
	"kms_decrypt": kmsDecrypt,
}
<|endoftext|>


<commit_before>package game

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"strings"

	"golang.org/x/net/websocket"

	"github.com/ghthor/aodd/game/datastore"
	"github.com/ghthor/engine/net/encoding"
	"github.com/ghthor/engine/net/protocol"
	"github.com/ghthor/engine/rpg2d"
)

type LoginReq struct {
	Name     string `json:"name"`
	Password string `json:"password"`
}

type packetHandler func(actorHandler) (actorHandler, error)

type actorHandler struct {
	protocol.Conn
	handlePacket packetHandler

	sim       rpg2d.RunningSimulation
	datastore datastore.Datastore

	actor *actor
}

// Starts the packet handler loop.
// This function is blocking.
func (c actorHandler) run() (err error) {
	for {
		c, err = c.handlePacket(c)
		if err != nil {
			if c.actor != nil {
				c.sim.RemoveActor(c.actor)
			}
			break
		}
	}
	return
}

var ErrWebsocketClientDisconnected = errors.New("websocket client disconnected")

type ErrUnexpectedPacket struct {
	Handler packetHandler
	Packet  encoding.Packet
}

func (e ErrUnexpectedPacket) String() string {
	return fmt.Sprint("unexpected packet {%v} in %v", e.Packet, e.Handler)
}

func (e ErrUnexpectedPacket) Error() string {
	return e.String()
}

// An implementation of packetHandler which
// will handle an actor logging in.
func (c actorHandler) loginHandler() (actorHandler, error) {
	packet, err := c.Read()
	if err != nil {
		return c, err
	}

	if packet.Type == encoding.PT_DISCONNECT {
		return c, ErrWebsocketClientDisconnected
	}

	switch packet.Type {
	case encoding.PT_JSON:
		switch packet.Msg {
		case "login":
			return c.respondToLoginReq(packet)
		case "create":
			return c.respondToCreateReq(packet)
		default:
		}
	default:
	}

	// TODO Improve this message with how to login
	c.SendMessage("notLoggedIn", "")
	return c, nil
}

// A login request is a event that can modify the
// state of the packet handler. If the login is
// successful the packet handler will transition
// to the input handler..
func (c actorHandler) respondToLoginReq(p encoding.Packet) (actorHandler, error) {
	r := LoginReq{}

	err := json.Unmarshal([]byte(p.Payload), &r)
	if err != nil {
		return c, errors.New(fmt.Sprint("error parsing login request:", err))
	}

	actor, exists := c.datastore.ActorExists(r.Name)
	if !exists {
		log.Printf("login failed: actor %s doesn't exist", r.Name)
		c.SendJson("actorDoesntExist", r)
		return c, nil
	}

	if !actor.Authenticate(r.Name, r.Password) {
		log.Printf("login failed: password for %s was incorrect", r.Name)
		c.SendMessage("authFailed", r.Name)
		return c, nil
	}

	c = c.loginActor(actor)

	log.Print("login success: ", r.Name)
	c.SendJson("loginSuccess", c.actor.ToState())
	return c, nil
}

// A create request is an event that can modify th
// state of the packet handler. If the create is
// successful the packet handler will transition
// in the input handler.
func (c actorHandler) respondToCreateReq(p encoding.Packet) (actorHandler, error) {
	r := LoginReq{}

	err := json.Unmarshal([]byte(p.Payload), &r)
	if err != nil {
		// TODO determine if this an error that should terminate the connection
		return c, errors.New(fmt.Sprint("error parsing login request:", err))
	}

	_, exists := c.datastore.ActorExists(r.Name)
	if exists {
		log.Printf("create failed: actor %s already exists", r.Name)
		c.SendMessage("actorAlreadyExists", "actor already exists")
		return c, nil
	}

	actor, err := c.datastore.AddActor(r.Name, r.Password)
	if err != nil {
		// TODO Instead of terminating the connection here
		// we should retry contacting the database or something
		return c, err
	}

	c = c.loginActor(actor)

	log.Print("created actor: ", actor.Name)

	c.SendJson("createSuccess", c.actor.ToState())
	return c, nil
}

// Creates a new actor struct using a datastore.Actor struct.
// Adds this new actor into the simulation.
func (c actorHandler) loginActor(dsactor datastore.Actor) actorHandler {
	// Set the actor this connection is now associated with
	// Mutate the packet handler into the next state
	c.handlePacket = (actorHandler).inputHandler

	// Create an actorEntity for this object
	c.actor = &actor{
		actorEntity{
			id: dsactor.Id,

			name: dsactor.Name,

			cell:   dsactor.Loc,
			facing: dsactor.Facing,
		},

		newActorConn(c),

		actorCmdRequest{},
	}

	c.sim.ConnectActor(c.actor)
	return c
}

// An implementation of packetHandler which will
// process input requests and prepare them
// for consumption by the input phase.
func (c actorHandler) inputHandler() (actorHandler, error) {
	packet, err := c.Read()
	if err != nil {
		return c, err
	}

	if packet.Type == encoding.PT_DISCONNECT {
		return c, ErrWebsocketClientDisconnected
	}

	switch packet.Type {
	case encoding.PT_MESSAGE:
		if strings.Contains(packet.Msg, "move") {
			err := c.actor.SubmitCmd(packet.Msg, packet.Payload)
			if err != nil {
				c.SendError("invalidActorCommand", err.Error())
			}
			return c, nil
		}
	default:
	}

	c.SendMessage("alreadyLoggedIn", "an actor has already been logged into this connection")
	return c, nil
}

// Return the actor bound to the connection.
func (c actorHandler) Actor() datastore.Actor {
	if c.actor == nil {
		return datastore.Actor{}
	}

	return datastore.Actor{
		Id: c.actor.id,

		Name: c.actor.name,

		Loc:    c.actor.cell,
		Facing: c.actor.facing,
	}
}

func newWebsocketActorHandler(sim rpg2d.RunningSimulation, datastore datastore.Datastore) websocket.Handler {
	return func(ws *websocket.Conn) {
		err := actorHandler{
			Conn:         protocol.NewWebsocketConn(ws),
			handlePacket: (actorHandler).loginHandler,

			sim:       sim,
			datastore: datastore,
		}.run()

		// TODO Maybe send a http response if there is an error
		if err != nil {
			log.Printf("disconnected: %v", err)
		}
	}
}
<commit_msg>Vanity spacing<commit_after>package game

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"strings"

	"golang.org/x/net/websocket"

	"github.com/ghthor/aodd/game/datastore"
	"github.com/ghthor/engine/net/encoding"
	"github.com/ghthor/engine/net/protocol"
	"github.com/ghthor/engine/rpg2d"
)

type LoginReq struct {
	Name     string `json:"name"`
	Password string `json:"password"`
}

type packetHandler func(actorHandler) (actorHandler, error)

type actorHandler struct {
	protocol.Conn
	handlePacket packetHandler

	sim       rpg2d.RunningSimulation
	datastore datastore.Datastore

	actor *actor
}

// Starts the packet handler loop.
// This function is blocking.
func (c actorHandler) run() (err error) {
	for {
		c, err = c.handlePacket(c)
		if err != nil {
			if c.actor != nil {
				c.sim.RemoveActor(c.actor)
			}
			break
		}
	}
	return
}

var ErrWebsocketClientDisconnected = errors.New("websocket client disconnected")

type ErrUnexpectedPacket struct {
	Handler packetHandler
	Packet  encoding.Packet
}

func (e ErrUnexpectedPacket) String() string {
	return fmt.Sprint("unexpected packet {%v} in %v", e.Packet, e.Handler)
}

func (e ErrUnexpectedPacket) Error() string {
	return e.String()
}

// An implementation of packetHandler which
// will handle an actor logging in.
func (c actorHandler) loginHandler() (actorHandler, error) {
	packet, err := c.Read()
	if err != nil {
		return c, err
	}

	if packet.Type == encoding.PT_DISCONNECT {
		return c, ErrWebsocketClientDisconnected
	}

	switch packet.Type {
	case encoding.PT_JSON:
		switch packet.Msg {
		case "login":
			return c.respondToLoginReq(packet)
		case "create":
			return c.respondToCreateReq(packet)
		default:
		}
	default:
	}

	// TODO Improve this message with how to login
	c.SendMessage("notLoggedIn", "")
	return c, nil
}

// A login request is a event that can modify the
// state of the packet handler. If the login is
// successful the packet handler will transition
// to the input handler..
func (c actorHandler) respondToLoginReq(p encoding.Packet) (actorHandler, error) {
	r := LoginReq{}

	err := json.Unmarshal([]byte(p.Payload), &r)
	if err != nil {
		return c, errors.New(fmt.Sprint("error parsing login request:", err))
	}

	actor, exists := c.datastore.ActorExists(r.Name)
	if !exists {
		log.Printf("login failed: actor %s doesn't exist", r.Name)
		c.SendJson("actorDoesntExist", r)
		return c, nil
	}

	if !actor.Authenticate(r.Name, r.Password) {
		log.Printf("login failed: password for %s was incorrect", r.Name)
		c.SendMessage("authFailed", r.Name)
		return c, nil
	}

	c = c.loginActor(actor)

	log.Print("login success: ", r.Name)

	c.SendJson("loginSuccess", c.actor.ToState())
	return c, nil
}

// A create request is an event that can modify th
// state of the packet handler. If the create is
// successful the packet handler will transition
// in the input handler.
func (c actorHandler) respondToCreateReq(p encoding.Packet) (actorHandler, error) {
	r := LoginReq{}

	err := json.Unmarshal([]byte(p.Payload), &r)
	if err != nil {
		// TODO determine if this an error that should terminate the connection
		return c, errors.New(fmt.Sprint("error parsing login request:", err))
	}

	_, exists := c.datastore.ActorExists(r.Name)
	if exists {
		log.Printf("create failed: actor %s already exists", r.Name)
		c.SendMessage("actorAlreadyExists", "actor already exists")
		return c, nil
	}

	actor, err := c.datastore.AddActor(r.Name, r.Password)
	if err != nil {
		// TODO Instead of terminating the connection here
		// we should retry contacting the database or something
		return c, err
	}

	c = c.loginActor(actor)

	log.Print("created actor: ", actor.Name)

	c.SendJson("createSuccess", c.actor.ToState())
	return c, nil
}

// Creates a new actor struct using a datastore.Actor struct.
// Adds this new actor into the simulation.
func (c actorHandler) loginActor(dsactor datastore.Actor) actorHandler {
	// Set the actor this connection is now associated with
	// Mutate the packet handler into the next state
	c.handlePacket = (actorHandler).inputHandler

	// Create an actorEntity for this object
	c.actor = &actor{
		actorEntity{
			id: dsactor.Id,

			name: dsactor.Name,

			cell:   dsactor.Loc,
			facing: dsactor.Facing,
		},

		newActorConn(c),

		actorCmdRequest{},
	}

	c.sim.ConnectActor(c.actor)
	return c
}

// An implementation of packetHandler which will
// process input requests and prepare them
// for consumption by the input phase.
func (c actorHandler) inputHandler() (actorHandler, error) {
	packet, err := c.Read()
	if err != nil {
		return c, err
	}

	if packet.Type == encoding.PT_DISCONNECT {
		return c, ErrWebsocketClientDisconnected
	}

	switch packet.Type {
	case encoding.PT_MESSAGE:
		if strings.Contains(packet.Msg, "move") {
			err := c.actor.SubmitCmd(packet.Msg, packet.Payload)
			if err != nil {
				c.SendError("invalidActorCommand", err.Error())
			}
			return c, nil
		}
	default:
	}

	c.SendMessage("alreadyLoggedIn", "an actor has already been logged into this connection")
	return c, nil
}

// Return the actor bound to the connection.
func (c actorHandler) Actor() datastore.Actor {
	if c.actor == nil {
		return datastore.Actor{}
	}

	return datastore.Actor{
		Id: c.actor.id,

		Name: c.actor.name,

		Loc:    c.actor.cell,
		Facing: c.actor.facing,
	}
}

func newWebsocketActorHandler(sim rpg2d.RunningSimulation, datastore datastore.Datastore) websocket.Handler {
	return func(ws *websocket.Conn) {
		err := actorHandler{
			Conn:         protocol.NewWebsocketConn(ws),
			handlePacket: (actorHandler).loginHandler,

			sim:       sim,
			datastore: datastore,
		}.run()

		// TODO Maybe send a http response if there is an error
		if err != nil {
			log.Printf("disconnected: %v", err)
		}
	}
}
<|endoftext|>


<commit_before>package sous

import (
	"fmt"
	"io"
	"math"
	"strconv"

	"github.com/opentable/sous/util/logging"
)

type (
	// Resources is a mapping of resource name to value, used to provision
	// single instances of an application. It is validated against
	// State.Defs.Resources. The keys must match defined resource names, and the
	// values must parse to the defined types.
	Resources map[string]string

	// A MissingResourceFlaw captures the absence of a required resource field,
	// and tries to repair it from the state defaults
	MissingResourceFlaw struct {
		Resources
		ClusterName    string
		Field, Default string
	}
)

// Clone returns a deep copy of this Resources.
func (r Resources) Clone() Resources {
	rs := make(Resources, len(r))
	for name, value := range r {
		rs[name] = value
	}
	return rs
}

// AddContext implements Flaw.AddContext.
func (f *MissingResourceFlaw) AddContext(name string, i interface{}) {
	if name == "cluster" {
		if name, is := i.(string); is {
			f.ClusterName = name
		}
	}
	/*
		// I'd misremembered that the State.Defs held the GDM-wide defaults
		// which isn't true. Leaving this here to sort of demostrate the idea
		if name != "state" {
			return
		}
		if state, is := i.(*State); is {
			f.State = state
		}
	*/
}

func (f *MissingResourceFlaw) String() string {
	name := f.ClusterName
	if name == "" {
		name = "??"
	}
	return fmt.Sprintf("Missing resource field %q for cluster %s", f.Field, name)
}

// Repair adds all missing fields set to default values.
func (f *MissingResourceFlaw) Repair() error {
	f.Resources[f.Field] = f.Default
	return nil
}

// Validate checks that each required resource value is set in this Resources,
// or in the inherited Resources.
func (r Resources) Validate() []Flaw {
	var flaws []Flaw

	if f := r.validateField("cpus", "0.1"); f != nil {
		flaws = append(flaws, f)
	}
	if f := r.validateField("memory", "100"); f != nil {
		flaws = append(flaws, f)
	}
	if f := r.validateField("ports", "1"); f != nil {
		flaws = append(flaws, f)
	}

	return flaws
}

func (r Resources) validateField(name, def string) Flaw {
	if _, has := r[name]; !has {
		return &MissingResourceFlaw{Resources: r, Field: name, Default: def}
	}
	return nil
}

// Cpus returns the number of CPUs.
func (r Resources) Cpus() float64 {
	cpuStr, present := r["cpus"]
	cpus, err := strconv.ParseFloat(cpuStr, 64)
	if err != nil {
		cpus = 0.1
		if present {
			reportResourceMessage(fmt.Sprintf("Could not parse value: '%s' for cpus as a float, using default: %f", cpuStr, cpus), r, logging.Log)
		} else {
			reportResourceMessage(fmt.Sprintf("Using default value for cpus: %f.", cpus), r, logging.Log, true)
		}
	}
	return cpus
}

// Memory returns memory in MB.
func (r Resources) Memory() float64 {
	memStr, present := r["memory"]
	memory, err := strconv.ParseFloat(memStr, 64)
	if err != nil {
		memory = 100
		if present {
			reportResourceMessage(fmt.Sprintf("Could not parse value: '%s' for memory as an int, using default: %f", memStr, memory), r, logging.Log)
		} else {
			reportResourceMessage(fmt.Sprintf("Using default value for memory: %f.", memory), r, logging.Log, true)
		}
	}
	return memory
}

// Ports returns the number of ports required.
func (r Resources) Ports() int32 {
	portStr, present := r["ports"]
	ports, err := strconv.ParseInt(portStr, 10, 32)
	if err != nil {
		ports = 1
		if present {
			reportResourceMessage(fmt.Sprintf("Could not parse value: '%s' for ports as a int, using default: %d", portStr, ports), r, logging.Log)
		} else {
			reportResourceMessage(fmt.Sprintf("Using default value for ports: %d", ports), r, logging.Log, true)
		}
	}
	return int32(ports)
}

// Equal checks equivalence between resource maps
func (r Resources) Equal(o Resources) bool {
	reportResourceMessage(fmt.Sprintf("Comparing resources: %+ v ?= %+ v", r, o), r, logging.Log, true)
	if len(r) != len(o) {
		reportResourceMessage("Lengths differ", r, logging.Log, true)
		return false
	}

	if r.Ports() != o.Ports() {
		reportResourceMessage("Ports differ", r, logging.Log, true)
		return false
	}

	if math.Abs(r.Cpus()-o.Cpus()) > 0.001 {
		reportResourceMessage("Cpus differ", r, logging.Log, true)
		return false
	}

	if math.Abs(r.Memory()-o.Memory()) > 0.001 {
		reportResourceMessage("Memory differ", r, logging.Log, true)
		return false
	}

	return true
}

type resourceMessage struct {
	logging.CallerInfo
	msg        string
	ports      int32
	cpus       float64
	memory     float64
	isDebugMsg bool
}

func reportResourceMessage(msg string, r Resources, log logging.LogSink, debug ...bool) {
	debugStmt := false
	if len(debug) > 0 {
		debugStmt = debug[0]
	}

	//not going to call Ports/Cpus/Memory to get values since those functions actually call reportResourceMessage
	var ports int32
	if portStr, present := r["ports"]; present {
		ports64, _ := strconv.ParseInt(portStr, 10, 32)
		ports = int32(ports64)
	}
	var memory float64
	if memStr, present := r["memory"]; present {
		memory, _ = strconv.ParseFloat(memStr, 64)
	}
	var cpus float64
	if cpuStr, present := r["cpus"]; present {
		cpus, _ = strconv.ParseFloat(cpuStr, 64)
	}

	msgLog := resourceMessage{
		msg:        msg,
		CallerInfo: logging.GetCallerInfo(logging.NotHere()),
		ports:      ports,
		cpus:       cpus,
		memory:     memory,
		isDebugMsg: debugStmt,
	}
	logging.Deliver(msgLog, log)
}

func (msg resourceMessage) WriteToConsole(console io.Writer) {
	fmt.Fprintf(console, "%s\n", msg.composeMsg())
}

func (msg resourceMessage) DefaultLevel() logging.Level {
	level := logging.WarningLevel
	if msg.isDebugMsg {
		level = logging.DebugLevel
	}

	return level
}

func (msg resourceMessage) Message() string {
	return msg.composeMsg()
}

func (msg resourceMessage) composeMsg() string {
	return fmt.Sprintf("%s: ports %v, cpus %v memory %v", msg.msg, msg.ports, msg.cpus, msg.memory)
}

func (msg resourceMessage) EachField(f logging.FieldReportFn) {
	f("@loglov3-otl", "sous-generic-v1")
	f("ports", msg.ports)
	f("cpus", msg.cpus)
	f("memory", msg.memory)
	msg.CallerInfo.EachField(f)
}
<commit_msg>added resourcemessage commit<commit_after>package sous

import (
	"fmt"
	"io"
	"math"
	"strconv"

	"github.com/opentable/sous/util/logging"
)

type (
	// Resources is a mapping of resource name to value, used to provision
	// single instances of an application. It is validated against
	// State.Defs.Resources. The keys must match defined resource names, and the
	// values must parse to the defined types.
	Resources map[string]string

	// A MissingResourceFlaw captures the absence of a required resource field,
	// and tries to repair it from the state defaults
	MissingResourceFlaw struct {
		Resources
		ClusterName    string
		Field, Default string
	}
)

// Clone returns a deep copy of this Resources.
func (r Resources) Clone() Resources {
	rs := make(Resources, len(r))
	for name, value := range r {
		rs[name] = value
	}
	return rs
}

// AddContext implements Flaw.AddContext.
func (f *MissingResourceFlaw) AddContext(name string, i interface{}) {
	if name == "cluster" {
		if name, is := i.(string); is {
			f.ClusterName = name
		}
	}
	/*
		// I'd misremembered that the State.Defs held the GDM-wide defaults
		// which isn't true. Leaving this here to sort of demostrate the idea
		if name != "state" {
			return
		}
		if state, is := i.(*State); is {
			f.State = state
		}
	*/
}

func (f *MissingResourceFlaw) String() string {
	name := f.ClusterName
	if name == "" {
		name = "??"
	}
	return fmt.Sprintf("Missing resource field %q for cluster %s", f.Field, name)
}

// Repair adds all missing fields set to default values.
func (f *MissingResourceFlaw) Repair() error {
	f.Resources[f.Field] = f.Default
	return nil
}

// Validate checks that each required resource value is set in this Resources,
// or in the inherited Resources.
func (r Resources) Validate() []Flaw {
	var flaws []Flaw

	if f := r.validateField("cpus", "0.1"); f != nil {
		flaws = append(flaws, f)
	}
	if f := r.validateField("memory", "100"); f != nil {
		flaws = append(flaws, f)
	}
	if f := r.validateField("ports", "1"); f != nil {
		flaws = append(flaws, f)
	}

	return flaws
}

func (r Resources) validateField(name, def string) Flaw {
	if _, has := r[name]; !has {
		return &MissingResourceFlaw{Resources: r, Field: name, Default: def}
	}
	return nil
}

// Cpus returns the number of CPUs.
func (r Resources) Cpus() float64 {
	cpuStr, present := r["cpus"]
	cpus, err := strconv.ParseFloat(cpuStr, 64)
	if err != nil {
		cpus = 0.1
		if present {
			reportResourceMessage(fmt.Sprintf("Could not parse value: '%s' for cpus as a float, using default: %f", cpuStr, cpus), r, logging.Log)
		} else {
			reportDebugResourceMessage(fmt.Sprintf("Using default value for cpus: %f.", cpus), r, logging.Log)
		}
	}
	return cpus
}

// Memory returns memory in MB.
func (r Resources) Memory() float64 {
	memStr, present := r["memory"]
	memory, err := strconv.ParseFloat(memStr, 64)
	if err != nil {
		memory = 100
		if present {
			reportResourceMessage(fmt.Sprintf("Could not parse value: '%s' for memory as an int, using default: %f", memStr, memory), r, logging.Log)
		} else {
			reportDebugResourceMessage(fmt.Sprintf("Using default value for memory: %f.", memory), r, logging.Log)
		}
	}
	return memory
}

// Ports returns the number of ports required.
func (r Resources) Ports() int32 {
	portStr, present := r["ports"]
	ports, err := strconv.ParseInt(portStr, 10, 32)
	if err != nil {
		ports = 1
		if present {
			reportResourceMessage(fmt.Sprintf("Could not parse value: '%s' for ports as a int, using default: %d", portStr, ports), r, logging.Log)
		} else {
			reportDebugResourceMessage(fmt.Sprintf("Using default value for ports: %d", ports), r, logging.Log)
		}
	}
	return int32(ports)
}

// Equal checks equivalence between resource maps
func (r Resources) Equal(o Resources) bool {
	reportDebugResourceMessage(fmt.Sprintf("Comparing resources: %+ v ?= %+ v", r, o), r, logging.Log)
	if len(r) != len(o) {
		reportDebugResourceMessage("Lengths differ", r, logging.Log)
		return false
	}

	if r.Ports() != o.Ports() {
		reportDebugResourceMessage("Ports differ", r, logging.Log)
		return false
	}

	if math.Abs(r.Cpus()-o.Cpus()) > 0.001 {
		reportDebugResourceMessage("Cpus differ", r, logging.Log)
		return false
	}

	if math.Abs(r.Memory()-o.Memory()) > 0.001 {
		reportDebugResourceMessage("Memory differ", r, logging.Log)
		return false
	}

	return true
}

type resourceMessage struct {
	logging.CallerInfo
	msg        string
	ports      int32
	cpus       float64
	memory     float64
	isDebugMsg bool
}

func reportDebugResourceMessage(msg string, r Resources, log logging.LogSink) {
	reportResourceMessage(msg, r, log, true)
}

func reportResourceMessage(msg string, r Resources, log logging.LogSink, debug ...bool) {
	debugStmt := false
	if len(debug) > 0 {
		debugStmt = debug[0]
	}

	//not going to call Ports/Cpus/Memory to get values since those functions actually call reportResourceMessage
	var ports int32
	if portStr, present := r["ports"]; present {
		ports64, _ := strconv.ParseInt(portStr, 10, 32)
		ports = int32(ports64)
	}
	var memory float64
	if memStr, present := r["memory"]; present {
		memory, _ = strconv.ParseFloat(memStr, 64)
	}
	var cpus float64
	if cpuStr, present := r["cpus"]; present {
		cpus, _ = strconv.ParseFloat(cpuStr, 64)
	}

	msgLog := resourceMessage{
		msg:        msg,
		CallerInfo: logging.GetCallerInfo(logging.NotHere()),
		ports:      ports,
		cpus:       cpus,
		memory:     memory,
		isDebugMsg: debugStmt,
	}
	logging.Deliver(msgLog, log)
}

func (msg resourceMessage) WriteToConsole(console io.Writer) {
	fmt.Fprintf(console, "%s\n", msg.composeMsg())
}

func (msg resourceMessage) DefaultLevel() logging.Level {
	level := logging.WarningLevel
	if msg.isDebugMsg {
		level = logging.DebugLevel
	}

	return level
}

func (msg resourceMessage) Message() string {
	return msg.composeMsg()
}

func (msg resourceMessage) composeMsg() string {
	return fmt.Sprintf("%s: ports %v, cpus %v memory %v", msg.msg, msg.ports, msg.cpus, msg.memory)
}

func (msg resourceMessage) EachField(f logging.FieldReportFn) {
	f("@loglov3-otl", "sous-generic-v1")
	f("ports", msg.ports)
	f("cpus", msg.cpus)
	f("memory", msg.memory)
	msg.CallerInfo.EachField(f)
}
<|endoftext|>