mirror of
https://github.com/hybridgroup/gobot.git
synced 2025-04-29 13:49:14 +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
|
package gobot
|
||||||
|
|
||||||
import "sync"
|
// Event represents when something asyncronous happens in a Driver
|
||||||
|
// or Adaptor
|
||||||
type callback struct {
|
|
||||||
f func(interface{})
|
|
||||||
once bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event executes the list of Callbacks when Chan is written to.
|
|
||||||
type Event struct {
|
type Event struct {
|
||||||
sync.Mutex
|
Name string
|
||||||
Callbacks []callback
|
Data interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEvent returns a new Event which is now listening for data.
|
// NewEvent returns a new Event and its associated data.
|
||||||
func NewEvent() *Event {
|
func NewEvent(name string, data interface{}) *Event {
|
||||||
return &Event{}
|
return &Event{Name: name, Data: data}
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
108
eventer.go
108
eventer.go
@ -1,36 +1,112 @@
|
|||||||
package gobot
|
package gobot
|
||||||
|
|
||||||
|
type eventChannel chan *Event
|
||||||
|
|
||||||
type eventer struct {
|
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
|
// Eventer is the interface which describes how a Driver or Adaptor
|
||||||
// which uses events.
|
// handles events.
|
||||||
type Eventer interface {
|
type Eventer interface {
|
||||||
// Events returns the Event map.
|
// Events returns the map of valid Event names.
|
||||||
Events() (events map[string]*Event)
|
Events() (eventnames map[string]string)
|
||||||
// Event returns an Event by name. Returns nil if the Event is not found.
|
// Event returns the map of valid Event names.
|
||||||
Event(name string) (event *Event)
|
Event(name string) string
|
||||||
// AddEvent adds a new Event given a name.
|
// AddEvent registers a new Event name.
|
||||||
AddEvent(name string)
|
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.
|
// NewEventer returns a new Eventer.
|
||||||
func NewEventer() Eventer {
|
func NewEventer() Eventer {
|
||||||
return &eventer{
|
evtr := &eventer{
|
||||||
events: make(map[string]*Event),
|
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 {
|
func (e *eventer) Events() map[string]string {
|
||||||
return e.events
|
return e.eventnames
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *eventer) Event(name string) (event *Event) {
|
func (e *eventer) Event(name string) string {
|
||||||
event, _ = e.events[name]
|
return e.eventnames[name]
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *eventer) AddEvent(name string) {
|
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 (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hybridgroup/gobot/gobottest"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEventer(t *testing.T) {
|
func TestEventer(t *testing.T) {
|
||||||
@ -11,12 +9,6 @@ func TestEventer(t *testing.T) {
|
|||||||
e.AddEvent("test")
|
e.AddEvent("test")
|
||||||
|
|
||||||
if _, ok := e.Events()["test"]; !ok {
|
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() {
|
func ExampleRand() {
|
||||||
i := gobot.Rand(100)
|
i := gobot.Rand(100)
|
||||||
fmt.Printf("%v is > 0 && < 100", i)
|
fmt.Printf("%v is > 0 && < 100", i)
|
||||||
|
@ -63,10 +63,10 @@ func (a *AnalogSensorDriver) Start() (errs []error) {
|
|||||||
for {
|
for {
|
||||||
newValue, err := a.Read()
|
newValue, err := a.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gobot.Publish(a.Event(Error), err)
|
a.Publish(a.Event(Error), err)
|
||||||
} else if newValue != value && newValue != -1 {
|
} else if newValue != value && newValue != -1 {
|
||||||
value = newValue
|
value = newValue
|
||||||
gobot.Publish(a.Event(Data), value)
|
a.Publish(a.Event(Data), value)
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case <-time.After(a.interval):
|
case <-time.After(a.interval):
|
||||||
|
@ -39,7 +39,7 @@ func TestAnalogSensorDriverStart(t *testing.T) {
|
|||||||
gobottest.Assert(t, len(d.Start()), 0)
|
gobottest.Assert(t, len(d.Start()), 0)
|
||||||
|
|
||||||
// data was received
|
// 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)
|
gobottest.Assert(t, data.(int), 100)
|
||||||
sem <- true
|
sem <- true
|
||||||
})
|
})
|
||||||
@ -56,7 +56,7 @@ func TestAnalogSensorDriverStart(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// read error
|
// 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")
|
gobottest.Assert(t, data.(error).Error(), "read error")
|
||||||
sem <- true
|
sem <- true
|
||||||
})
|
})
|
||||||
@ -73,7 +73,7 @@ func TestAnalogSensorDriverStart(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// send a halt message
|
// send a halt message
|
||||||
gobot.Once(d.Event(Data), func(data interface{}) {
|
d.Once(d.Event(Data), func(data interface{}) {
|
||||||
sem <- true
|
sem <- true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
package gpio
|
package gpio
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/hybridgroup/gobot"
|
"github.com/hybridgroup/gobot"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ gobot.Driver = (*ButtonDriver)(nil)
|
var _ gobot.Driver = (*ButtonDriver)(nil)
|
||||||
@ -58,7 +57,7 @@ func (b *ButtonDriver) Start() (errs []error) {
|
|||||||
for {
|
for {
|
||||||
newValue, err := b.connection.DigitalRead(b.Pin())
|
newValue, err := b.connection.DigitalRead(b.Pin())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gobot.Publish(b.Event(Error), err)
|
b.Publish(b.Event(Error), err)
|
||||||
} else if newValue != state && newValue != -1 {
|
} else if newValue != state && newValue != -1 {
|
||||||
state = newValue
|
state = newValue
|
||||||
b.update(newValue)
|
b.update(newValue)
|
||||||
@ -91,9 +90,9 @@ func (b *ButtonDriver) Connection() gobot.Connection { return b.connection.(gobo
|
|||||||
func (b *ButtonDriver) update(newValue int) {
|
func (b *ButtonDriver) update(newValue int) {
|
||||||
if newValue == 1 {
|
if newValue == 1 {
|
||||||
b.Active = true
|
b.Active = true
|
||||||
gobot.Publish(b.Event(Push), newValue)
|
b.Publish(b.Event(Push), newValue)
|
||||||
} else {
|
} else {
|
||||||
b.Active = false
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
gobot.Once(d.Event(Push), func(data interface{}) {
|
d.Once(d.Event(Push), func(data interface{}) {
|
||||||
gobottest.Assert(t, d.Active, true)
|
gobottest.Assert(t, d.Active, true)
|
||||||
sem <- true
|
sem <- true
|
||||||
})
|
})
|
||||||
@ -58,7 +58,7 @@ func TestButtonDriverStart(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
gobot.Once(d.Event(Release), func(data interface{}) {
|
d.Once(d.Event(Release), func(data interface{}) {
|
||||||
gobottest.Assert(t, d.Active, false)
|
gobottest.Assert(t, d.Active, false)
|
||||||
sem <- true
|
sem <- true
|
||||||
})
|
})
|
||||||
@ -74,7 +74,7 @@ func TestButtonDriverStart(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
gobot.Once(d.Event(Error), func(data interface{}) {
|
d.Once(d.Event(Error), func(data interface{}) {
|
||||||
sem <- true
|
sem <- true
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ func TestButtonDriverStart(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
gobot.Once(d.Event(Push), func(data interface{}) {
|
d.Once(d.Event(Push), func(data interface{}) {
|
||||||
sem <- true
|
sem <- true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -111,11 +111,11 @@ func NewGrovePiezoVibrationSensorDriver(a AnalogReader, name string, pin string,
|
|||||||
|
|
||||||
sensor.AddEvent(Vibration)
|
sensor.AddEvent(Vibration)
|
||||||
|
|
||||||
gobot.On(sensor.Event(Data), func(data interface{}) {
|
// sensor.On(sensor.Event(Data), func(data interface{}) {
|
||||||
if data.(int) > 1000 {
|
// if data.(int) > 1000 {
|
||||||
gobot.Publish(sensor.Event(Vibration), data)
|
// sensor.Publish(sensor.Event(Vibration), data)
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
|
|
||||||
return sensor
|
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
|
newValue := 1/(math.Log(resistance/10000.0)/thermistor+1/298.15) - 273.15
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gobot.Publish(a.Event(Error), err)
|
a.Publish(a.Event(Error), err)
|
||||||
} else if newValue != a.temperature && newValue != -1 {
|
} else if newValue != a.temperature && newValue != -1 {
|
||||||
a.temperature = newValue
|
a.temperature = newValue
|
||||||
gobot.Publish(a.Event(Data), a.temperature)
|
a.Publish(a.Event(Data), a.temperature)
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case <-time.After(a.interval):
|
case <-time.After(a.interval):
|
||||||
|
@ -67,15 +67,15 @@ func (b *MakeyButtonDriver) Start() (errs []error) {
|
|||||||
for {
|
for {
|
||||||
newValue, err := b.connection.DigitalRead(b.Pin())
|
newValue, err := b.connection.DigitalRead(b.Pin())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gobot.Publish(b.Event(Error), err)
|
b.Publish(b.Event(Error), err)
|
||||||
} else if newValue != state && newValue != -1 {
|
} else if newValue != state && newValue != -1 {
|
||||||
state = newValue
|
state = newValue
|
||||||
if newValue == 0 {
|
if newValue == 0 {
|
||||||
b.Active = true
|
b.Active = true
|
||||||
gobot.Publish(b.Event(Push), newValue)
|
b.Publish(b.Event(Push), newValue)
|
||||||
} else {
|
} else {
|
||||||
b.Active = false
|
b.Active = false
|
||||||
gobot.Publish(b.Event(Release), newValue)
|
b.Publish(b.Event(Release), newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
|
27
utils.go
27
utils.go
@ -51,33 +51,6 @@ func After(t time.Duration, f func()) {
|
|||||||
time.AfterFunc(t, f)
|
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
|
// Rand returns a positive random int up to max
|
||||||
func Rand(max int) int {
|
func Rand(max int) int {
|
||||||
i, _ := rand.Int(rand.Reader, big.NewInt(int64(max)))
|
i, _ := rand.Int(rand.Reader, big.NewInt(int64(max)))
|
||||||
|
@ -46,66 +46,6 @@ func TestAfter(t *testing.T) {
|
|||||||
gobottest.Assert(t, i, 1)
|
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) {
|
func TestFromScale(t *testing.T) {
|
||||||
gobottest.Assert(t, FromScale(5, 0, 10), 0.5)
|
gobottest.Assert(t, FromScale(5, 0, 10), 0.5)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user