From a4b611252e3392e1624fcd11fd21fd50a9635cc9 Mon Sep 17 00:00:00 2001 From: Jake Brukhman Date: Sat, 25 Feb 2012 19:17:54 -0500 Subject: [PATCH] Added ConnectRaw method. --- examples/abort/abort.go | 42 ++++++---- examples/streamer/streamer.go | 22 +++-- goneuro.go | 149 +++++++++++++++------------------- 3 files changed, 106 insertions(+), 107 deletions(-) diff --git a/examples/abort/abort.go b/examples/abort/abort.go index fa94790..f941f53 100644 --- a/examples/abort/abort.go +++ b/examples/abort/abort.go @@ -5,44 +5,50 @@ import ( "fmt" "os" "time" + "flag" ) -const SERIAL_PORT = "/dev/tty.MindBand" +const DEFAULT_PORT = "/dev/tty.MindBand2" +var serialPort *string = flag.String("port", DEFAULT_PORT, "the serial port for the device") -func signalHandler(disconnect chan<- bool, ch chan byte) { +func init() { + flag.Parse() +} + +func signalHandler(d *goneuro.Device, data chan byte) { println("sleeping 10 seconds...") - time.Sleep(2 * 1e9) + time.Sleep(10 * time.Second) println("disconnecting...") - disconnect <- true + d.Disconnect() println("closing ch") - close(ch) + close(data) } func main() { // collect meditation on a channel - ch := make(chan byte, 512) + data := make(chan byte, 512) listener := &goneuro.ThinkGearListener{ Meditation: func(b byte) { - ch <- b + data <- b }, } - - // open the device - disconnect, err := goneuro.Connect(SERIAL_PORT, listener) - if err != nil { - println("couldn't connect to device") - os.Exit(1) - } + println("getting device and connecting...") + d := goneuro.NewDevice(*serialPort) + if err := d.Connect(listener); err != nil { + fmt.Println(err) + os.Exit(1) + } + d.Engage() // listen for Ctrl-C - go signalHandler(disconnect, ch) + go signalHandler(d, data) // wait for and print values indefinitely for { - b, ok := <-ch + b, ok := <-data if !ok { - println("will die in 2") - time.Sleep(1e9 * 2) + println("will die in 5") + time.Sleep(1e9 * 5) break // we are done } fmt.Println("Meditation: ", b) diff --git a/examples/streamer/streamer.go b/examples/streamer/streamer.go index f93e194..3b17bf7 100644 --- a/examples/streamer/streamer.go +++ b/examples/streamer/streamer.go @@ -4,9 +4,16 @@ import ( "fmt" "github.com/jbrukh/goneuro" "time" + "flag" + "os" ) -const SERIAL_PORT = "/dev/tty.MindBand2" +const DEFAULT_PORT = "/dev/tty.MindBand2" +var serialPort *string = flag.String("port", DEFAULT_PORT, "the serial port for the device") + +func init() { + flag.Parse() +} func main() { data := make(chan int16) @@ -15,12 +22,15 @@ func main() { data <- int16(a)<<8 | int16(b) }, } - _, err := goneuro.Connect(SERIAL_PORT, listener) - if err != nil { - fmt.Println(err) - return - } + d := goneuro.NewDevice(*serialPort) + if err := d.Connect(listener); err != nil { + os.Exit(1) + } + println("sleeping 5 seconds") + time.Sleep(5*time.Second) + println("engaging") + d.Engage() startNanos := time.Now() for { fmt.Println(time.Now().Sub(startNanos).Nanoseconds(), <-data) diff --git a/goneuro.go b/goneuro.go index 056a263..bdfcb9d 100644 --- a/goneuro.go +++ b/goneuro.go @@ -14,7 +14,7 @@ import ( "fmt" "io" "os" - "github.com/jbrukh/atomic" + "sync" ) // Approx the number of data points to be @@ -123,7 +123,7 @@ func parseByteStream(device io.ReadCloser, pparser payloadParser, conn <-chan bo // check for exit select { case v, ok := <-conn: - if ok & !v { + if ok && !v { return // disconnect when "false" sent } else if ok && v { engaged = true @@ -171,8 +171,8 @@ func parseByteStream(device io.ReadCloser, pparser payloadParser, conn <-chan bo println("checksum has failed: ", checksum, "expected: ", stated) continue } - if payloadParser != nil { - payloadParser(&payload) + if pparser != nil { + pparser(&payload) } } println("done with parsing") @@ -180,7 +180,7 @@ func parseByteStream(device io.ReadCloser, pparser payloadParser, conn <-chan bo // fullPayloadParser delivers a payload parser with the // given listener -func fullPayloadParser(listener *ThinkGearListener) { +func fullPayloadParser(listener *ThinkGearListener) payloadParser { return func(payloadPtr *[]byte) { parseFullPayload(payloadPtr, listener) } @@ -189,7 +189,7 @@ func fullPayloadParser(listener *ThinkGearListener) { // rawPayloadParser delivers a payload parser that only // parses raw signal and ignores everything else, and then // delivers the raw signal on a channel -func rawPayloadParser(output chan float64) { +func rawPayloadParser(output chan<- float64) payloadParser { return func(payloadPtr *[]byte) { parseRawPayload(payloadPtr, output) } @@ -286,8 +286,8 @@ func parseRawPayload(payloadPtr *[]byte, output chan<- float64) { // get the data output <- float64(int16(payload[inx+2])<<8 | int16(payload[inx+3])) } else { - println("raw signal did not have 2 bytes") - break + println("raw signal did not have 2 bytes") + break } nextRow(4) default: @@ -297,17 +297,18 @@ func parseRawPayload(payloadPtr *[]byte, output chan<- float64) { } // Device represents a NeuroSky/ThinkGear device -type Device { - conn chan<- bool +type Device struct { + conn chan bool Port string - connected *atomic.AtomicValue + connected bool + lock *sync.Mutex } -func (d *Device) New(serialPort string) *Device { +func NewDevice(serialPort string) *Device { return &Device{ - conn: make(chan bool) + conn: make(chan bool), Port: serialPort, - connected: atomic.NewWithValue(false) + lock: new(sync.Mutex), } } @@ -317,8 +318,10 @@ func (d *Device) New(serialPort string) *Device { // If the device is not connected, then this call // will have no effect. func (d *Device) Engage() { - if (d.connected.Get().(bool)) { - conn <- true + d.lock.Lock() + defer d.lock.Unlock() + if (d.connected) { + d.conn <- true } } @@ -326,8 +329,11 @@ func (d *Device) Engage() { // close the serial port. If the device is not // connected, this call will have no effect. func (d *Device) Disconnect() { - if (d.connected.Get().(bool)) { - conn <- false + d.lock.Lock() // otherwise, multiple calls here can block forever + defer d.lock.Unlock() + if (d.connected) { + d.conn <- false + d.connected = false } } @@ -342,22 +348,47 @@ func (d *Device) Disconnect() { // // If the device is already connected, then this call // will have no effect. +// +// The serial port is typically a string of the form +// +// /dev/tty.MindBand +// +// or whatever you set up in your systems Bluetooth +// options for the device. Note that the various +// portions of the ThinkGearListener will be triggered +// synchronously to parsing, so it may be desirable +// in certain situations for the user to throw data +// onto channels for serial, asynchronous processing. +// If you do use a channel, make sure that this channel +// is asynchronous, or you can still hold up processing. func (d *Device) Connect(listener *ThinkGearListener) (err error) { - if (d.connected.Get().(bool)) { - return errors.New("device is already connected") + d.lock.Lock() // multiple connects on the same device will block + defer d.lock.Unlock() + + device, err := d.connect() + if err != nil { + return + } + // start spinning the data stream on another thread + // and wait for Engage() call + go parseByteStream(device, fullPayloadParser(listener), d.conn) + return +} + +// connect will connect to the serial port and set internal +// state of the Device appropriately. This method probably +// needs to be synchronized externally. +func (d *Device) connect() (device io.ReadCloser, err error) { + if (d.connected) { + return nil, errors.New("device is already connected") } - var device io.ReadCloser device, err = os.Open(d.Port) if err != nil { str := fmt.Sprintf("device problem: %s", err) - return errors.New(str) + return nil, errors.New(str) } - d.connected.Set(true) + d.connected = true println("connected: ", d.Port) - - // start spinning the data stream on another thread - // and wait for Engage() call - go parseByteStream(device, fullPayloadParser(listener), d.conn) return } @@ -370,64 +401,16 @@ func (d *Device) Connect(listener *ThinkGearListener) (err error) { // // If the device is already connected, then this call // will have no effect. -func (d *Device) ConnectRaw(output chan<- float64) { - if (d.connected.Get().(bool)) { - return - } -} +func (d *Device) ConnectRaw(output chan<- float64) (err error){ + d.lock.Lock() // multiple connects on the same device will block + defer d.lock.Unlock() -// Connect to the device over the serial port -// and start parsing data; the serial port -// is typically a string of the form -// -// /dev/tty.MindBand -// -// or whatever you set up in your systems Bluetooth -// options for the device. Note that the various -// portions of the ThinkGearListener will be triggered -// synchronously to parsing, so it may be desirable -// in certain situations for the user to throw data -// onto channels for serial, asynchronous processing. -// If you do use a channel, make sure that this channel -// is asynchronous, or you can still hold up processing. -// -// This method will return a send-only channel -// for the purposes of ceasing the connection. In -// order to close the connection, send true to -// the disconnect channel. -func Connect(serialPort string, listener *ThinkGearListener) (conn chan<- bool, err error) { - var device io.ReadCloser - device, err = os.Open(serialPort) + device, err := d.connect() if err != nil { - str := fmt.Sprintf("device problem: %s", err) - return nil, errors.New(str) + return } - println("connected: ", serialPort) - - // create the disconnect channel - ch := make(chan bool) - - // go and process this this stream asynchronously - // until the user sends a signal to disconnect - go parseByteStream(device, fullPayloadParser(listener), ch) - - disconnect = ch // cast to send-only + // start spinning the data stream on another thread + // and wait for Engage() call + go parseByteStream(device, rawPayloadParser(output), d.conn) return -} - -// ConnectRaw streams just the raw data on a channel; -// this is provided as a convenience method -func ConnectRaw(serialPort string) (disconnect chan<- bool, data <-chan float64, err error) { - ch := make(chan float64, WINDOW_SIZE) - listener := &ThinkGearListener{ - RawSignal: func(a, b byte) { - ch <- float64(int16(a)<<8 | int16(b)) - }, - } - disconnect, err = Connect(serialPort, listener) - if err != nil { - return - } - data = ch - return } \ No newline at end of file