1
0
mirror of https://github.com/hybridgroup/gobot.git synced 2025-05-02 22:17:12 +08:00

Merge branch 'dev' into refactor_interfaces

Conflicts:
	adaptor.go
	driver.go
	gobot.go
	robot.go
	test_helper.go
	utils.go
This commit is contained in:
Adrian Zankich 2014-11-20 18:08:50 -08:00
commit a0a1322fb1
11 changed files with 177 additions and 80 deletions

View File

@ -1,3 +1,8 @@
0.7.1
---
- opencv
- Fix pthread_create issue on Mac OS
0.7 0.7
--- ---
- Dramatically increased test coverage and documentation - Dramatically increased test coverage and documentation

View File

@ -98,9 +98,12 @@ Gobot has a extensible system for connecting to hardware devices. The following
- [Intel Edison](http://www.intel.com/content/www/us/en/do-it-yourself/edison.html) <=> [Library](https://github.com/hybridgroup/gobot/tree/master/platforms/intel-iot/edison) - [Intel Edison](http://www.intel.com/content/www/us/en/do-it-yourself/edison.html) <=> [Library](https://github.com/hybridgroup/gobot/tree/master/platforms/intel-iot/edison)
- [Joystick](http://en.wikipedia.org/wiki/Joystick) <=> [Library](https://github.com/hybridgroup/gobot/tree/master/platforms/joystick) - [Joystick](http://en.wikipedia.org/wiki/Joystick) <=> [Library](https://github.com/hybridgroup/gobot/tree/master/platforms/joystick)
- [Leap Motion](https://www.leapmotion.com/) <=> [Library](https://github.com/hybridgroup/gobot/tree/master/platforms/leapmotion) - [Leap Motion](https://www.leapmotion.com/) <=> [Library](https://github.com/hybridgroup/gobot/tree/master/platforms/leapmotion)
- [MavLink](http://qgroundcontrol.org/mavlink/start) <=> [Library](https://github.com/hybridgroup/gobot/tree/master/platforms/mavlinky)
- [MQTT](http://mqtt.org/) <=> [Library](https://github.com/hybridgroup/gobot/tree/master/platforms/mqtt)
- [Neurosky](http://neurosky.com/products-markets/eeg-biosensors/hardware/) <=> [Library](https://github.com/hybridgroup/gobot/tree/master/platforms/neurosky) - [Neurosky](http://neurosky.com/products-markets/eeg-biosensors/hardware/) <=> [Library](https://github.com/hybridgroup/gobot/tree/master/platforms/neurosky)
- [OpenCV](http://opencv.org/) <=> [Library](https://github.com/hybridgroup/gobot/tree/master/platforms/opencv) - [OpenCV](http://opencv.org/) <=> [Library](https://github.com/hybridgroup/gobot/tree/master/platforms/opencv)
- [Pebble](https://www.getpebble.com/) <=> [Library](https://github.com/hybridgroup/gobot/tree/master/platforms/pebble) - [Pebble](https://www.getpebble.com/) <=> [Library](https://github.com/hybridgroup/gobot/tree/master/platforms/pebble)
- [Raspberry Pi](http://www.raspberrypi.org/) <=> [Library](https://github.com/hybridgroup/gobot/tree/master/platforms/raspi)
- [Spark](https://www.spark.io/) <=> [Library](https://github.com/hybridgroup/gobot/tree/master/platforms/spark) - [Spark](https://www.spark.io/) <=> [Library](https://github.com/hybridgroup/gobot/tree/master/platforms/spark)
- [Sphero](http://www.gosphero.com/) <=> [Library](https://github.com/hybridgroup/gobot/tree/master/platforms/sphero) - [Sphero](http://www.gosphero.com/) <=> [Library](https://github.com/hybridgroup/gobot/tree/master/platforms/sphero)
@ -125,13 +128,15 @@ drivers provided using the gobot-i2c module:
- [I2C](https://en.wikipedia.org/wiki/I%C2%B2C) <=> [Drivers](https://github.com/hybridgroup/gobot/tree/master/platforms/i2c) - [I2C](https://en.wikipedia.org/wiki/I%C2%B2C) <=> [Drivers](https://github.com/hybridgroup/gobot/tree/master/platforms/i2c)
- BlinkM - BlinkM
- HMC6352 - HMC6352
- MPL1150A2
- MPU6050
- Wii Nunchuck Controller - Wii Nunchuck Controller
More platforms and drivers are coming soon... More platforms and drivers are coming soon...
## Getting Started ## Getting Started
Install Gobot with: `go get -u github.com/hybridgroup/gobot` Install Gobot with: `go get -d -u github.com/hybridgroup/gobot/...`
## API: ## API:

78
doc.go
View File

@ -2,14 +2,9 @@
/* /*
Package gobot provides a framework for robotics, physical computing and the internet of things. Package gobot provides a framework for robotics, physical computing and the internet of things.
It is the main point of entry in your Gobot application. A Gobot It is the main point of entry for your Gobot application. A Gobot program
is typically composed of one or more robots that makes up a project. is typically composed of one or more robots that makes up a project.
Commands are a way to expose your robots functionality with the external world.
A Gobot can be configured to expose a restful HTTP interface using the api
package. You can define custom commands on your Gobot and interact with your
application as a web service.
Basic Setup Basic Setup
package main package main
@ -35,39 +30,7 @@ Basic Setup
gbot.Start() gbot.Start()
} }
Web Enabled? You bet! Blinking an LED (Hello Eve!)
package main
import (
"fmt"
"github.com/hybridgroup/gobot"
"github.com/hybridgroup/gobot/api"
)
func main() {
gbot := gobot.NewGobot()
// Starts the API server on default port 3000
api.NewAPI(gbot).Start()
// Accessible via http://localhost:3000/api/commands/say_hello
gbot.AddCommand("say_hello", func(params map[string]interface{}) interface{} {
return "Master says hello!"
})
hello := gbot.AddRobot(gobot.NewRobot("Eve"))
// Accessible via http://localhost:3000/robots/Eve/commands/say_hello
hello.AddCommand("say_hello", func(params map[string]interface{}) interface{} {
return fmt.Sprintf("%v says hello!", hello.Name)
})
gbot.Start()
}
Blinking teh LED (Hello Eve!)
package main package main
@ -101,5 +64,42 @@ Blinking teh LED (Hello Eve!)
gbot.Start() gbot.Start()
} }
Web Enabled? You bet! Gobot can be configured to expose a restful HTTP interface
using the api package. You can define custom commands on your robots, in addition
to the built-in device driver commands, and interact with your application as a
web service.
package main
import (
"fmt"
"github.com/hybridgroup/gobot"
"github.com/hybridgroup/gobot/api"
)
func main() {
gbot := gobot.NewGobot()
// Starts the API server on default port 3000
api.NewAPI(gbot).Start()
// Accessible via http://localhost:3000/api/commands/say_hello
gbot.AddCommand("say_hello", func(params map[string]interface{}) interface{} {
return "Master says hello!"
})
hello := gbot.AddRobot(gobot.NewRobot("Eve"))
// Accessible via http://localhost:3000/robots/Eve/commands/say_hello
hello.AddCommand("say_hello", func(params map[string]interface{}) interface{} {
return fmt.Sprintf("%v says hello!", hello.Name)
})
gbot.Start()
}
*/ */
package gobot package gobot

View File

@ -10,8 +10,7 @@ type Event struct {
Callbacks []callback Callbacks []callback
} }
// NewEvent generates a new event by making a channel // NewEvent returns a new event which is then ready for publishing and subscribing.
// and start reading from it
func NewEvent() *Event { func NewEvent() *Event {
e := &Event{ e := &Event{
Chan: make(chan interface{}, 1), Chan: make(chan interface{}, 1),
@ -25,7 +24,7 @@ func NewEvent() *Event {
return e return e
} }
// Writes sends event data to channel // Write writes data to the Event
func (e *Event) Write(data interface{}) { func (e *Event) Write(data interface{}) {
select { select {
case e.Chan <- data: case e.Chan <- data:
@ -33,8 +32,7 @@ func (e *Event) Write(data interface{}) {
} }
} }
// Read waits data from channel and execute callbacks // Read publishes to all subscribers of e if there is any new data
// for each event when received
func (e *Event) Read() { func (e *Event) Read() {
for s := range e.Chan { for s := range e.Chan {
tmp := []callback{} tmp := []callback{}

75
examples_test.go Normal file
View File

@ -0,0 +1,75 @@
package gobot_test
import (
"fmt"
"github.com/hybridgroup/gobot"
"testing"
"time"
)
func ExampleEvery() {
gobot.Every(1*time.Second, func() {
fmt.Println("Hello")
})
}
func ExampleAfter() {
gobot.After(1*time.Second, func() {
fmt.Println("Hello")
})
}
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.Sprintln("%v is > 0 && < 100", i)
}
func ExampleFromScale() {
fmt.Println(gobot.FromScale(5, 0, 10))
// Output:
// 0.5
}
func ExampleToScale() {
fmt.Println(gobot.ToScale(500, 0, 10))
// Output:
// 10
}
func ExampleAssert() {
t := &testing.T{}
var a int = 100
var b int = 100
gobot.Assert(t, a, b)
}
func ExampleRefute() {
t := &testing.T{}
var a int = 100
var b int = 200
gobot.Refute(t, a, b)
}

View File

@ -20,7 +20,7 @@ type Gobot struct {
Eventer Eventer
} }
// NewGobot instantiates a new Gobot // NewGobot returns a new Gobot
func NewGobot() *Gobot { func NewGobot() *Gobot {
return &Gobot{ return &Gobot{
robots: &robots{}, robots: &robots{},
@ -32,7 +32,9 @@ func NewGobot() *Gobot {
} }
} }
// Start runs the main Gobot event loop // Start calls the Start method on each robot in it's collection of robots, and
// stops all robots on reception of a SIGINT. Start will block the execution of
// your main function until it receives the SIGINT.
func (g *Gobot) Start() (errs []error) { func (g *Gobot) Start() (errs []error) {
if rerrs := g.robots.Start(); len(rerrs) > 0 { if rerrs := g.robots.Start(); len(rerrs) > 0 {
for _, err := range rerrs { for _, err := range rerrs {
@ -69,18 +71,19 @@ func (g *Gobot) Start() (errs []error) {
return errs return errs
} }
// Robots fetch all robots associated with this Gobot instance. // Robots returns all robots associated with this Gobot instance.
func (g *Gobot) Robots() *robots { func (g *Gobot) Robots() *robots {
return g.robots return g.robots
} }
// AddRobot adds a new robot to our Gobot instance. // AddRobot adds a new robot to the internal collection of robots. Returns the
// added robot
func (g *Gobot) AddRobot(r *Robot) *Robot { func (g *Gobot) AddRobot(r *Robot) *Robot {
*g.robots = append(*g.robots, r) *g.robots = append(*g.robots, r)
return r return r
} }
// Robot find a robot with a given name. // Robot returns a robot given name. Returns nil on no robot.
func (g *Gobot) Robot(name string) *Robot { func (g *Gobot) Robot(name string) *Robot {
for _, robot := range *g.Robots() { for _, robot := range *g.Robots() {
if robot.Name == name { if robot.Name == name {
@ -90,7 +93,7 @@ func (g *Gobot) Robot(name string) *Robot {
return nil return nil
} }
// ToJSON retrieves a JSON representation of this Gobot. // ToJSON returns a JSON representation of this Gobot.
func (g *Gobot) ToJSON() *JSONGobot { func (g *Gobot) ToJSON() *JSONGobot {
jsonGobot := &JSONGobot{ jsonGobot := &JSONGobot{
Robots: []*JSONRobot{}, Robots: []*JSONRobot{},

View File

@ -6,7 +6,7 @@ This package provides the Gobot adaptor for the [Beaglebone Black](http://beagle
## Installing ## Installing
``` ```
go get github.com/hybridgroup/gobot && go install github.com/hybridgroup/platforms/gobot/beaglebone go get -d -u github.com/hybridgroup/gobot/... && go install github.com/hybridgroup/gobot/platforms/beaglebone
``` ```
## Cross compiling for the Beaglebone Black ## Cross compiling for the Beaglebone Black

View File

@ -66,6 +66,7 @@ func (f *FirmataAdaptor) Connect() (errs []error) {
if err := f.connect(f); err != nil { if err := f.connect(f); err != nil {
return []error{err} return []error{err}
} }
f.board.connect()
f.SetConnected(true) f.SetConnected(true)
return return
} }

View File

@ -5,6 +5,7 @@ import (
cv "github.com/hybridgroup/go-opencv/opencv" cv "github.com/hybridgroup/go-opencv/opencv"
"github.com/hybridgroup/gobot" "github.com/hybridgroup/gobot"
"time"
) )
var _ gobot.DriverInterface = (*CameraDriver)(nil) var _ gobot.DriverInterface = (*CameraDriver)(nil)
@ -49,14 +50,17 @@ func (c *CameraDriver) Start() (errs []error) {
if err := c.start(c); err != nil { if err := c.start(c); err != nil {
return []error{err} return []error{err}
} }
gobot.Every(c.Interval(), func() { go func() {
for {
if c.camera.GrabFrame() { if c.camera.GrabFrame() {
image := c.camera.RetrieveFrame(1) image := c.camera.RetrieveFrame(1)
if image != nil { if image != nil {
gobot.Publish(c.Event("frame"), image) gobot.Publish(c.Event("frame"), image)
} }
} }
}) <-time.After(c.Interval())
}
}()
return return
} }

View File

@ -14,9 +14,9 @@ type JSONRobot struct {
Devices []*JSONDevice `json:"devices"` Devices []*JSONDevice `json:"devices"`
} }
// Robot software representation of a physical board. A robot is a named // Robot is a named entitity that manages a collection of connections and devices.
// entitity that manages multiple IO devices using a set of adaptors. Additionally // It containes it's own work routine and a collection of
// a user can specificy custom commands to control a robot remotely. // custom commands to control a robot remotely via the Gobot api.
type Robot struct { type Robot struct {
Name string Name string
Work func() Work func()
@ -54,8 +54,11 @@ func (r *robots) Each(f func(*Robot)) {
} }
} }
// NewRobot constructs a new named robot. Though a robot's name will be generated, // NewRobot returns a new Robot given a name and optionally accepts:
// we recommend that user take care of naming a robot for later access. //
// []Connection: Connections which are automatically started and stopped with the robot
// []Device: Devices which are automatically started and stopped with the robot
// func(): The work routine the robot will execute once all devices and connections have been initialized and started
func NewRobot(name string, v ...interface{}) *Robot { func NewRobot(name string, v ...interface{}) *Robot {
if name == "" { if name == "" {
name = fmt.Sprintf("%X", Rand(int(^uint(0)>>1))) name = fmt.Sprintf("%X", Rand(int(^uint(0)>>1)))
@ -116,18 +119,19 @@ func (r *Robot) Start() (errs []error) {
return return
} }
// Devices retrieves all devices associated with this robot. // Devices returns all devices associated with this robot.
func (r *Robot) Devices() *devices { func (r *Robot) Devices() *devices {
return r.devices return r.devices
} }
// AddDevice adds a new device on this robot. // AddDevice adds a new device to the robots collection of devices. Returns the
// added device.
func (r *Robot) AddDevice(d Device) Device { func (r *Robot) AddDevice(d Device) Device {
*r.devices = append(*r.Devices(), d) *r.devices = append(*r.Devices(), d)
return d return d
} }
// Device finds a device by name. // Device returns a device given a name. Returns nil on no device.
func (r *Robot) Device(name string) Device { func (r *Robot) Device(name string) Device {
if r == nil { if r == nil {
return nil return nil
@ -140,18 +144,19 @@ func (r *Robot) Device(name string) Device {
return nil return nil
} }
// Connections retrieves all connections on this robot. // Connections returns all connections associated with this robot.
func (r *Robot) Connections() *connections { func (r *Robot) Connections() *connections {
return r.connections return r.connections
} }
// AddConnection add a new connection on this robot. // AddConnection adds a new connection to the robots collection of connections.
// Returns the added connection.
func (r *Robot) AddConnection(c Connection) Connection { func (r *Robot) AddConnection(c Connection) Connection {
*r.connections = append(*r.Connections(), c) *r.connections = append(*r.Connections(), c)
return c return c
} }
// Connection finds a connection by name. // Connection returns a connection given a name. Returns nil on no connection.
func (r *Robot) Connection(name string) Connection { func (r *Robot) Connection(name string) Connection {
if r == nil { if r == nil {
return nil return nil
@ -164,7 +169,7 @@ func (r *Robot) Connection(name string) Connection {
return nil return nil
} }
// ToJSON returns a JSON representation of the master robot. // ToJSON returns a JSON representation of the robot.
func (r *Robot) ToJSON() *JSONRobot { func (r *Robot) ToJSON() *JSONRobot {
jsonRobot := &JSONRobot{ jsonRobot := &JSONRobot{
Name: r.Name, Name: r.Name,

View File

@ -42,7 +42,8 @@ func Refute(t *testing.T, a interface{}, b interface{}) {
} }
} }
// Every triggers f every `t` time until the end of days. // Every triggers f every t time until the end of days. It does not wait for the
// previous execution of f to finish before it fires the next f.
func Every(t time.Duration, f func()) { func Every(t time.Duration, f func()) {
c := time.Tick(t) c := time.Tick(t)
@ -54,12 +55,12 @@ func Every(t time.Duration, f func()) {
}() }()
} }
// After triggers the passed function after `t` duration. // After triggers f after t duration.
func After(t time.Duration, f func()) { func After(t time.Duration, f func()) {
time.AfterFunc(t, f) time.AfterFunc(t, f)
} }
// Publish emits an event by writting value // Publish emits val to all subscribers of e.
func Publish(e *Event, val interface{}) (err error) { func Publish(e *Event, val interface{}) (err error) {
if err = eventError(e); err == nil { if err = eventError(e); err == nil {
e.Write(val) e.Write(val)
@ -67,7 +68,7 @@ func Publish(e *Event, val interface{}) (err error) {
return return
} }
// On adds `f` to callbacks that are executed on specified event // On executes f when e is Published to.
func On(e *Event, f func(s interface{})) (err error) { func On(e *Event, f func(s interface{})) (err error) {
if err = eventError(e); err == nil { if err = eventError(e); err == nil {
e.Callbacks = append(e.Callbacks, callback{f, false}) e.Callbacks = append(e.Callbacks, callback{f, false})
@ -75,8 +76,7 @@ func On(e *Event, f func(s interface{})) (err error) {
return return
} }
// Once adds `f` to callbacks that are executed on specified event // Once is similar to On except that it only executes f one time.
// and sets flag to be called only once
func Once(e *Event, f func(s interface{})) (err error) { func Once(e *Event, f func(s interface{})) (err error) {
if err = eventError(e); err == nil { if err = eventError(e); err == nil {
e.Callbacks = append(e.Callbacks, callback{f, true}) e.Callbacks = append(e.Callbacks, callback{f, true})
@ -84,19 +84,20 @@ func Once(e *Event, f func(s interface{})) (err error) {
return return
} }
// Rand generates random int lower than 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)))
return int(i.Int64()) return int(i.Int64())
} }
// FromScale creates a scale using min and max values // FromScale returns a converted input from min, max to 0.0...1.0.
// to be used in combination with ToScale
func FromScale(input, min, max float64) float64 { func FromScale(input, min, max float64) float64 {
return (input - math.Min(min, max)) / (math.Max(min, max) - math.Min(min, max)) return (input - math.Min(min, max)) / (math.Max(min, max) - math.Min(min, max))
} }
// ToScale is used with FromScale to return input converted to new scale // ToScale returns a converted input from 0...1 to min...max scale.
// If input is less than min then ToScale returns min.
// If input is greater than max then ToScale returns max
func ToScale(input, min, max float64) float64 { func ToScale(input, min, max float64) float64 {
i := input*(math.Max(min, max)-math.Min(min, max)) + math.Min(min, max) i := input*(math.Max(min, max)-math.Min(min, max)) + math.Min(min, max)
if i < math.Min(min, max) { if i < math.Min(min, max) {