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
---
- 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)
- [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)
- [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)
- [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)
- [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)
- [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)
- BlinkM
- HMC6352
- MPL1150A2
- MPU6050
- Wii Nunchuck Controller
More platforms and drivers are coming soon...
## Getting Started
Install Gobot with: `go get -u github.com/hybridgroup/gobot`
Install Gobot with: `go get -d -u github.com/hybridgroup/gobot/...`
## API:

78
doc.go
View File

@ -2,14 +2,9 @@
/*
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.
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
package main
@ -35,39 +30,7 @@ Basic Setup
gbot.Start()
}
Web Enabled? You bet!
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!)
Blinking an LED (Hello Eve!)
package main
@ -101,5 +64,42 @@ Blinking teh LED (Hello Eve!)
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

View File

@ -10,8 +10,7 @@ type Event struct {
Callbacks []callback
}
// NewEvent generates a new event by making a channel
// and start reading from it
// NewEvent returns a new event which is then ready for publishing and subscribing.
func NewEvent() *Event {
e := &Event{
Chan: make(chan interface{}, 1),
@ -25,7 +24,7 @@ func NewEvent() *Event {
return e
}
// Writes sends event data to channel
// Write writes data to the Event
func (e *Event) Write(data interface{}) {
select {
case e.Chan <- data:
@ -33,8 +32,7 @@ func (e *Event) Write(data interface{}) {
}
}
// Read waits data from channel and execute callbacks
// for each event when received
// Read publishes to all subscribers of e if there is any new data
func (e *Event) Read() {
for s := range e.Chan {
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
}
// NewGobot instantiates a new Gobot
// NewGobot returns a new Gobot
func NewGobot() *Gobot {
return &Gobot{
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) {
if rerrs := g.robots.Start(); len(rerrs) > 0 {
for _, err := range rerrs {
@ -69,18 +71,19 @@ func (g *Gobot) Start() (errs []error) {
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 {
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 {
*g.robots = append(*g.robots, 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 {
for _, robot := range *g.Robots() {
if robot.Name == name {
@ -90,7 +93,7 @@ func (g *Gobot) Robot(name string) *Robot {
return nil
}
// ToJSON retrieves a JSON representation of this Gobot.
// ToJSON returns a JSON representation of this Gobot.
func (g *Gobot) ToJSON() *JSONGobot {
jsonGobot := &JSONGobot{
Robots: []*JSONRobot{},

View File

@ -6,7 +6,7 @@ This package provides the Gobot adaptor for the [Beaglebone Black](http://beagle
## 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

View File

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

View File

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

View File

@ -14,9 +14,9 @@ type JSONRobot struct {
Devices []*JSONDevice `json:"devices"`
}
// Robot software representation of a physical board. A robot is a named
// entitity that manages multiple IO devices using a set of adaptors. Additionally
// a user can specificy custom commands to control a robot remotely.
// Robot is a named entitity that manages a collection of connections and devices.
// It containes it's own work routine and a collection of
// custom commands to control a robot remotely via the Gobot api.
type Robot struct {
Name string
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,
// we recommend that user take care of naming a robot for later access.
// NewRobot returns a new Robot given a name and optionally accepts:
//
// []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 {
if name == "" {
name = fmt.Sprintf("%X", Rand(int(^uint(0)>>1)))
@ -116,18 +119,19 @@ func (r *Robot) Start() (errs []error) {
return
}
// Devices retrieves all devices associated with this robot.
// Devices returns all devices associated with this robot.
func (r *Robot) Devices() *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 {
*r.devices = append(*r.Devices(), 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 {
if r == nil {
return nil
@ -140,18 +144,19 @@ func (r *Robot) Device(name string) Device {
return nil
}
// Connections retrieves all connections on this robot.
// Connections returns all connections associated with this robot.
func (r *Robot) Connections() *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 {
*r.connections = append(*r.Connections(), 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 {
if r == nil {
return nil
@ -164,7 +169,7 @@ func (r *Robot) Connection(name string) Connection {
return nil
}
// ToJSON returns a JSON representation of the master robot.
// ToJSON returns a JSON representation of the robot.
func (r *Robot) ToJSON() *JSONRobot {
jsonRobot := &JSONRobot{
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()) {
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()) {
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) {
if err = eventError(e); err == nil {
e.Write(val)
@ -67,7 +68,7 @@ func Publish(e *Event, val interface{}) (err error) {
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) {
if err = eventError(e); err == nil {
e.Callbacks = append(e.Callbacks, callback{f, false})
@ -75,8 +76,7 @@ func On(e *Event, f func(s interface{})) (err error) {
return
}
// Once adds `f` to callbacks that are executed on specified event
// and sets flag to be called only once
// Once is similar to On except that it only executes f one time.
func Once(e *Event, f func(s interface{})) (err error) {
if err = eventError(e); err == nil {
e.Callbacks = append(e.Callbacks, callback{f, true})
@ -84,19 +84,20 @@ func Once(e *Event, f func(s interface{})) (err error) {
return
}
// Rand generates random int lower than max
// Rand returns a positive random int up to max
func Rand(max int) int {
i, _ := rand.Int(rand.Reader, big.NewInt(int64(max)))
return int(i.Int64())
}
// FromScale creates a scale using min and max values
// to be used in combination with ToScale
// FromScale returns a converted input from min, max to 0.0...1.0.
func FromScale(input, min, max float64) float64 {
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 {
i := input*(math.Max(min, max)-math.Min(min, max)) + math.Min(min, max)
if i < math.Min(min, max) {