mirror of
https://github.com/hybridgroup/gobot.git
synced 2025-04-24 13:48:49 +08:00
core: Refactor events to use channels all the way down. Allows 'metal' development using Gobot libs.
Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
parent
22aeb498f4
commit
0e25f29a1b
36
event.go
36
event.go
@ -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
eventer.go
108
eventer.go
@ -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
|
||||
}
|
||||
|
@ -2,8 +2,6 @@ package gobot
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hybridgroup/gobot/gobottest"
|
||||
)
|
||||
|
||||
func TestEventer(t *testing.T) {
|
||||
@ -11,12 +9,6 @@ func TestEventer(t *testing.T) {
|
||||
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
examples/metal_button.go
Normal file
30
examples/metal_button.go
Normal file
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
})
|
||||
@ -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
|
||||
})
|
||||
@ -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
|
||||
})
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
package gpio
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hybridgroup/gobot"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ gobot.Driver = (*ButtonDriver)(nil)
|
||||
@ -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)
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
})
|
||||
@ -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
|
||||
})
|
||||
@ -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
|
||||
})
|
||||
|
||||
@ -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
|
||||
})
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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):
|
||||
|
@ -67,15 +67,15 @@ func (b *MakeyButtonDriver) 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
|
||||
if newValue == 0 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
select {
|
||||
|
27
utils.go
27
utils.go
@ -51,33 +51,6 @@ func After(t time.Duration, f func()) {
|
||||
time.AfterFunc(t, f)
|
||||
}
|
||||
|
||||
// Publish emits val to all subscribers of e. Returns ErrUnknownEvent if Event
|
||||
// does not exist.
|
||||
func Publish(e *Event, val interface{}) (err error) {
|
||||
if err = eventError(e); err == nil {
|
||||
e.Write(val)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// On executes f when e is Published to. Returns ErrUnknownEvent if Event
|
||||
// does not exist.
|
||||
func On(e *Event, f func(s interface{})) (err error) {
|
||||
if err = eventError(e); err == nil {
|
||||
e.Callbacks = append(e.Callbacks, callback{f, false})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Once is similar to On except that it only executes f one time. Returns
|
||||
//ErrUnknownEvent if Event does not exist.
|
||||
func Once(e *Event, f func(s interface{})) (err error) {
|
||||
if err = eventError(e); err == nil {
|
||||
e.Callbacks = append(e.Callbacks, callback{f, true})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Rand returns a positive random int up to max
|
||||
func Rand(max int) int {
|
||||
i, _ := rand.Int(rand.Reader, big.NewInt(int64(max)))
|
||||
|
@ -46,66 +46,6 @@ func TestAfter(t *testing.T) {
|
||||
gobottest.Assert(t, i, 1)
|
||||
}
|
||||
|
||||
func TestPublish(t *testing.T) {
|
||||
c := make(chan interface{}, 1)
|
||||
|
||||
cb := callback{
|
||||
f: func(val interface{}) {
|
||||
c <- val
|
||||
},
|
||||
}
|
||||
|
||||
e := &Event{Callbacks: []callback{cb}}
|
||||
Publish(e, 1)
|
||||
<-time.After(10 * time.Millisecond)
|
||||
Publish(e, 2)
|
||||
Publish(e, 3)
|
||||
Publish(e, 4)
|
||||
i := <-c
|
||||
gobottest.Assert(t, i, 1)
|
||||
|
||||
var e1 = (*Event)(nil)
|
||||
gobottest.Assert(t, Publish(e1, 4), ErrUnknownEvent)
|
||||
}
|
||||
|
||||
func TestOn(t *testing.T) {
|
||||
var i int
|
||||
e := NewEvent()
|
||||
On(e, func(data interface{}) {
|
||||
i = data.(int)
|
||||
})
|
||||
Publish(e, 10)
|
||||
<-time.After(1 * time.Millisecond)
|
||||
gobottest.Assert(t, i, 10)
|
||||
|
||||
var e1 = (*Event)(nil)
|
||||
err := On(e1, func(data interface{}) {
|
||||
i = data.(int)
|
||||
})
|
||||
gobottest.Assert(t, err, ErrUnknownEvent)
|
||||
}
|
||||
func TestOnce(t *testing.T) {
|
||||
i := 0
|
||||
e := NewEvent()
|
||||
Once(e, func(data interface{}) {
|
||||
i += data.(int)
|
||||
})
|
||||
On(e, func(data interface{}) {
|
||||
i += data.(int)
|
||||
})
|
||||
Publish(e, 10)
|
||||
<-time.After(1 * time.Millisecond)
|
||||
Publish(e, 10)
|
||||
<-time.After(1 * time.Millisecond)
|
||||
gobottest.Assert(t, i, 30)
|
||||
|
||||
var e1 = (*Event)(nil)
|
||||
err := Once(e1, func(data interface{}) {
|
||||
i = data.(int)
|
||||
})
|
||||
gobottest.Assert(t, err, ErrUnknownEvent)
|
||||
}
|
||||
|
||||
func TestFromScale(t *testing.T) {
|
||||
gobottest.Assert(t, FromScale(5, 0, 10), 0.5)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user