Skip to content

Commit

Permalink
core: Refactor events to use channels all the way down. Allows 'metal…
Browse files Browse the repository at this point in the history
…' development using Gobot libs.

Signed-off-by: deadprogram <[email protected]>
  • Loading branch information
deadprogram committed Sep 12, 2016
1 parent 22aeb49 commit 0e25f29
Show file tree
Hide file tree
Showing 14 changed files with 153 additions and 189 deletions.
36 changes: 7 additions & 29 deletions event.go
Original file line number Diff line number Diff line change
@@ -1,35 +1,13 @@
package gobot

import "sync"

type callback struct {
f func(interface{})
once bool
}

// Event executes the list of Callbacks when Chan is written to.
// Event represents when something asyncronous happens in a Driver
// or Adaptor
type Event struct {
sync.Mutex
Callbacks []callback
Name string
Data interface{}
}

// NewEvent returns a new Event which is now listening for data.
func NewEvent() *Event {
return &Event{}
}

// Write writes data to the Event, it will not block and will not buffer if there
// are no active subscribers to the Event.
func (e *Event) Write(data interface{}) {
e.Lock()
defer e.Unlock()

tmp := []callback{}
for _, cb := range e.Callbacks {
go cb.f(data)
if !cb.once {
tmp = append(tmp, cb)
}
}
e.Callbacks = tmp
// NewEvent returns a new Event and its associated data.
func NewEvent(name string, data interface{}) *Event {
return &Event{Name: name, Data: data}
}
108 changes: 92 additions & 16 deletions eventer.go
Original file line number Diff line number Diff line change
@@ -1,36 +1,112 @@
package gobot

type eventChannel chan *Event

type eventer struct {
events map[string]*Event
// map of valid Event names
eventnames map[string]string

// new events get put in to the event channel
in eventChannel

// slice of out channels used by subscribers
outs []eventChannel
}

// Eventer is the interface which describes behaviour for a Driver or Adaptor
// which uses events.
// Eventer is the interface which describes how a Driver or Adaptor
// handles events.
type Eventer interface {
// Events returns the Event map.
Events() (events map[string]*Event)
// Event returns an Event by name. Returns nil if the Event is not found.
Event(name string) (event *Event)
// AddEvent adds a new Event given a name.
// Events returns the map of valid Event names.
Events() (eventnames map[string]string)
// Event returns the map of valid Event names.
Event(name string) string
// AddEvent registers a new Event name.
AddEvent(name string)
// Publish new events to anyone listening
Publish(name string, data interface{})
// Subscribe to any events from this eventer
Subscribe() (events eventChannel)
}

// NewEventer returns a new Eventer.
func NewEventer() Eventer {
return &eventer{
events: make(map[string]*Event),
evtr := &eventer{
eventnames: make(map[string]string),
in: make(eventChannel, 1),
outs: make([]eventChannel, 1),
}

// goroutine to cascade in events to all out event channels
go func() {
for {
select {
case evt := <-evtr.in:
for _, out := range evtr.outs[1:] {
out <- evt
}
}
}
}()

return evtr
}

func (e *eventer) Events() map[string]*Event {
return e.events
func (e *eventer) Events() map[string]string {
return e.eventnames
}

func (e *eventer) Event(name string) (event *Event) {
event, _ = e.events[name]
return
func (e *eventer) Event(name string) string {
return e.eventnames[name]
}

func (e *eventer) AddEvent(name string) {
e.events[name] = NewEvent()
e.eventnames[name] = name
}

func (e *eventer) Publish(name string, data interface{}) {
evt := NewEvent(name, data)
e.in <- evt
}

func (e *eventer) Subscribe() eventChannel {
out := make(eventChannel)
e.outs = append(e.outs, out)
return out
}

// On executes f when e is Published to. Returns ErrUnknownEvent if Event
// does not exist.
func (e *eventer) On(n string, f func(s interface{})) (err error) {
out := e.Subscribe()
go func() {
for {
select {
case evt := <-out:
if evt.Name == n {
f(evt.Data)
}
}
}
}()

return
}

// Once is similar to On except that it only executes f one time. Returns
//ErrUnknownEvent if Event does not exist.
func (e *eventer) Once(n string, f func(s interface{})) (err error) {
out := e.Subscribe()
go func() {
for {
select {
case evt := <-out:
if evt.Name == n {
f(evt.Data)
break
}
}
}
}()

return
}
10 changes: 1 addition & 9 deletions eventer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,13 @@ package gobot

import (
"testing"

"github.com/hybridgroup/gobot/gobottest"
)

func TestEventer(t *testing.T) {
e := NewEventer()
e.AddEvent("test")

if _, ok := e.Events()["test"]; !ok {
t.Errorf("Could not add event to list of Events")
t.Errorf("Could not add event to list of Event names")
}

event := e.Event("test")
gobottest.Refute(t, event, nil)

event = e.Event("booyeah")
gobottest.Assert(t, event, (*Event)(nil))
}
30 changes: 30 additions & 0 deletions examples/metal_button.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package main

import (
"fmt"
"github.com/hybridgroup/gobot/platforms/gpio"
"github.com/hybridgroup/gobot/platforms/intel-iot/edison"
)

func main() {
e := edison.NewEdisonAdaptor("edison")
led := gpio.NewLedDriver(e, "led", "13")
button := gpio.NewButtonDriver(e, "button", "5")

e.Connect()
led.Start()
button.Start()

led.Off()

buttonEvents := button.Subscribe()
for {
select {
case event := <-buttonEvents:
fmt.Println("Event:", event.Name, event.Data)
if event.Name == "push" {
led.Toggle()
}
}
}
}
24 changes: 0 additions & 24 deletions examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,30 +21,6 @@ func ExampleAfter() {
})
}

func ExamplePublish() {
e := gobot.NewEvent()
gobot.Publish(e, 100)
}

func ExampleOn() {
e := gobot.NewEvent()
gobot.On(e, func(s interface{}) {
fmt.Println(s)
})
gobot.Publish(e, 100)
gobot.Publish(e, 200)
}

func ExampleOnce() {
e := gobot.NewEvent()
gobot.Once(e, func(s interface{}) {
fmt.Println(s)
fmt.Println("I will no longer respond to events")
})
gobot.Publish(e, 100)
gobot.Publish(e, 200)
}

func ExampleRand() {
i := gobot.Rand(100)
fmt.Printf("%v is > 0 && < 100", i)
Expand Down
4 changes: 2 additions & 2 deletions platforms/gpio/analog_sensor_driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ func (a *AnalogSensorDriver) Start() (errs []error) {
for {
newValue, err := a.Read()
if err != nil {
gobot.Publish(a.Event(Error), err)
a.Publish(a.Event(Error), err)
} else if newValue != value && newValue != -1 {
value = newValue
gobot.Publish(a.Event(Data), value)
a.Publish(a.Event(Data), value)
}
select {
case <-time.After(a.interval):
Expand Down
6 changes: 3 additions & 3 deletions platforms/gpio/analog_sensor_driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func TestAnalogSensorDriverStart(t *testing.T) {
gobottest.Assert(t, len(d.Start()), 0)

// data was received
gobot.Once(d.Event(Data), func(data interface{}) {
d.Once(d.Event(Data), func(data interface{}) {
gobottest.Assert(t, data.(int), 100)
sem <- true
})
Expand All @@ -56,7 +56,7 @@ func TestAnalogSensorDriverStart(t *testing.T) {
}

// read error
gobot.Once(d.Event(Error), func(data interface{}) {
d.Once(d.Event(Error), func(data interface{}) {
gobottest.Assert(t, data.(error).Error(), "read error")
sem <- true
})
Expand All @@ -73,7 +73,7 @@ func TestAnalogSensorDriverStart(t *testing.T) {
}

// send a halt message
gobot.Once(d.Event(Data), func(data interface{}) {
d.Once(d.Event(Data), func(data interface{}) {
sem <- true
})

Expand Down
9 changes: 4 additions & 5 deletions platforms/gpio/button_driver.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package gpio

import (
"time"

"github.com/hybridgroup/gobot"
"time"
)

var _ gobot.Driver = (*ButtonDriver)(nil)
Expand Down Expand Up @@ -58,7 +57,7 @@ func (b *ButtonDriver) Start() (errs []error) {
for {
newValue, err := b.connection.DigitalRead(b.Pin())
if err != nil {
gobot.Publish(b.Event(Error), err)
b.Publish(b.Event(Error), err)
} else if newValue != state && newValue != -1 {
state = newValue
b.update(newValue)
Expand Down Expand Up @@ -91,9 +90,9 @@ func (b *ButtonDriver) Connection() gobot.Connection { return b.connection.(gobo
func (b *ButtonDriver) update(newValue int) {
if newValue == 1 {
b.Active = true
gobot.Publish(b.Event(Push), newValue)
b.Publish(b.Event(Push), newValue)
} else {
b.Active = false
gobot.Publish(b.Event(Release), newValue)
b.Publish(b.Event(Release), newValue)
}
}
8 changes: 4 additions & 4 deletions platforms/gpio/button_driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func TestButtonDriverStart(t *testing.T) {
return
}

gobot.Once(d.Event(Push), func(data interface{}) {
d.Once(d.Event(Push), func(data interface{}) {
gobottest.Assert(t, d.Active, true)
sem <- true
})
Expand All @@ -58,7 +58,7 @@ func TestButtonDriverStart(t *testing.T) {
return
}

gobot.Once(d.Event(Release), func(data interface{}) {
d.Once(d.Event(Release), func(data interface{}) {
gobottest.Assert(t, d.Active, false)
sem <- true
})
Expand All @@ -74,7 +74,7 @@ func TestButtonDriverStart(t *testing.T) {
return
}

gobot.Once(d.Event(Error), func(data interface{}) {
d.Once(d.Event(Error), func(data interface{}) {
sem <- true
})

Expand All @@ -89,7 +89,7 @@ func TestButtonDriverStart(t *testing.T) {
return
}

gobot.Once(d.Event(Push), func(data interface{}) {
d.Once(d.Event(Push), func(data interface{}) {
sem <- true
})

Expand Down
10 changes: 5 additions & 5 deletions platforms/gpio/grove_drivers.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,11 @@ func NewGrovePiezoVibrationSensorDriver(a AnalogReader, name string, pin string,

sensor.AddEvent(Vibration)

gobot.On(sensor.Event(Data), func(data interface{}) {
if data.(int) > 1000 {
gobot.Publish(sensor.Event(Vibration), data)
}
})
// sensor.On(sensor.Event(Data), func(data interface{}) {
// if data.(int) > 1000 {
// sensor.Publish(sensor.Event(Vibration), data)
// }
// })

return sensor
}
Expand Down
4 changes: 2 additions & 2 deletions platforms/gpio/grove_temperature_sensor_driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ func (a *GroveTemperatureSensorDriver) Start() (errs []error) {
newValue := 1/(math.Log(resistance/10000.0)/thermistor+1/298.15) - 273.15

if err != nil {
gobot.Publish(a.Event(Error), err)
a.Publish(a.Event(Error), err)
} else if newValue != a.temperature && newValue != -1 {
a.temperature = newValue
gobot.Publish(a.Event(Data), a.temperature)
a.Publish(a.Event(Data), a.temperature)
}
select {
case <-time.After(a.interval):
Expand Down
Loading

0 comments on commit 0e25f29

Please sign in to comment.