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:
commit
a0a1322fb1
|
@ -1,3 +1,8 @@
|
|||
0.7.1
|
||||
---
|
||||
- opencv
|
||||
- Fix pthread_create issue on Mac OS
|
||||
|
||||
0.7
|
||||
---
|
||||
- Dramatically increased test coverage and documentation
|
||||
|
|
|
@ -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
78
doc.go
|
@ -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
|
||||
|
|
8
event.go
8
event.go
|
@ -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{}
|
||||
|
|
|
@ -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)
|
||||
}
|
15
gobot.go
15
gobot.go
|
@ -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{},
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
29
robot.go
29
robot.go
|
@ -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,
|
||||
|
|
21
utils.go
21
utils.go
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue