Merge pull request #3 from hybridgroup/api

Gobot RESful API
This commit is contained in:
Ron Evans 2013-11-24 16:41:33 -08:00
commit 66cfedbb63
10 changed files with 252 additions and 22 deletions

View File

@ -54,6 +54,20 @@ func main() {
robot.Start()
}
```
## API:
Gobot includes a RESTful API to query the status of any robot running within a group, including the connection and device status, and execute device commands.
To activate the API, use the `Api` command like this:
```go
master := gobot.GobotMaster()
gobot.Api(master)
```
To specify the api port run your Gobot program with the `PORT` environment variable
```
$ PORT=8080 go run gobotProgram.go
```
## Hardware Support
Gobot has a extensible system for connecting to hardware devices. The following robotics and physical computing platforms are currently supported:

60
api.go Normal file
View File

@ -0,0 +1,60 @@
package gobot
import (
"encoding/json"
"github.com/codegangsta/martini"
"net/http"
)
type api struct{}
func Api(bot *Master) {
a := new(api)
m := martini.Classic()
m.Get("/robots", func() string {
return toJson(bot.Robots)
})
m.Get("/robots/:robotname", func(params martini.Params) string {
return toJson(bot.FindRobot(params["robotname"]))
})
m.Get("/robots/:robotname/devices", func(params martini.Params) string {
return toJson(bot.FindRobot(params["robotname"]).GetDevices())
})
m.Get("/robots/:robotname/devices/:devicename", func(params martini.Params) string {
return toJson(bot.FindRobotDevice(params["robotname"], params["devicename"]))
})
m.Get("/robots/:robotname/devices/:devicename/commands", func(params martini.Params) string {
return toJson(bot.FindRobotDevice(params["robotname"], params["devicename"]).Commands())
})
command_route := "/robots/:robotname/devices/:devicename/commands/:command"
m.Get(command_route, func(params martini.Params, res http.ResponseWriter, req *http.Request) string {
return a.executeCommand(bot, params, res, req)
})
m.Post(command_route, func(params martini.Params, res http.ResponseWriter, req *http.Request) string {
return a.executeCommand(bot, params, res, req)
})
go m.Run()
}
func (a *api) executeCommand(bot *Master, params martini.Params, res http.ResponseWriter, req *http.Request) string {
decoder := json.NewDecoder(req.Body)
var body map[string]interface{}
decoder.Decode(&body)
robot := bot.FindRobotDevice(params["robotname"], params["devicename"])
commands := robot.Commands().([]string)
for command := range commands {
if commands[command] == params["command"] {
ret := Call(robot.Driver, params["command"], body)
return toJson(map[string]interface{}{"results": ret})
}
}
return toJson(map[string]interface{}{"results": "Unknown Command"})
}

View File

@ -9,7 +9,7 @@ type Connection struct {
Name string
Adaptor interface{}
Port string
Robot *Robot
Robot *Robot `json:"-"`
Params map[string]string
}

View File

@ -8,7 +8,7 @@ import (
type Device struct {
Name string
Interval string
Robot *Robot
Robot *Robot `json:"-"`
Driver interface{}
Params map[string]string
}
@ -29,6 +29,6 @@ func (d *Device) Start() {
}
}
func (d *Device) Command(method_name string, arguments []string) {
//dt.Driver.Command(method_name, arguments)
func (d *Device) Commands() interface{} {
return reflect.ValueOf(d.Driver).Elem().FieldByName("Commands").Interface()
}

View File

@ -7,7 +7,8 @@ type Driver struct {
Pin string
Name string
Params map[string]string
Events map[string]chan interface{}
Commands []string
Events map[string]chan interface{} `json:"-"`
}
func NewDriver(d Driver) Driver {

38
examples/sphero_api.go Normal file
View File

@ -0,0 +1,38 @@
package main
import (
"github.com/hybridgroup/gobot"
"github.com/hybridgroup/gobot-sphero"
)
func main() {
master := gobot.GobotMaster()
gobot.Api(master)
spheros := map[string]string{
"Sphero-BPO": "127.0.0.1:4560",
}
for name, port := range spheros {
spheroAdaptor := new(gobotSphero.SpheroAdaptor)
spheroAdaptor.Name = "sphero"
spheroAdaptor.Port = port
sphero := gobotSphero.NewSphero(spheroAdaptor)
sphero.Name = "sphero"
sphero.Interval = "0.5s"
work := func() {
sphero.SetRGB(uint8(255), uint8(0), uint8(0))
}
master.Robots = append(master.Robots, gobot.Robot{
Name: name,
Connections: []interface{}{spheroAdaptor},
Devices: []interface{}{sphero},
Work: work,
})
}
master.Start()
}

46
examples/sphero_master.go Normal file
View File

@ -0,0 +1,46 @@
package main
import (
"github.com/hybridgroup/gobot"
"github.com/hybridgroup/gobot-sphero"
)
func main() {
master := gobot.GobotMaster()
spheros := map[string]string{
"Sphero-BPO": "127.0.0.1:4560",
}
for name, port := range spheros {
spheroAdaptor := new(gobotSphero.SpheroAdaptor)
spheroAdaptor.Name = "sphero"
spheroAdaptor.Port = port
sphero := gobotSphero.NewSphero(spheroAdaptor)
sphero.Name = "sphero"
sphero.Interval = "0.5s"
work := func() {
sphero.SetRGB(uint8(255), uint8(0), uint8(0))
}
master.Robots = append(master.Robots, gobot.Robot{
Name: name,
Connections: []interface{}{spheroAdaptor},
Devices: []interface{}{sphero},
Work: work,
})
}
master.Robots = append(master.Robots, gobot.Robot{
Work: func() {
sphero := master.FindRobot("Sphero-BPO")
gobot.Every("1s", func() {
gobot.Call(sphero.GetDevice("sphero").Driver, "SetRGB", uint8(gobot.Rand(255)), uint8(gobot.Rand(255)), uint8(gobot.Rand(255)))
})
},
})
master.Start()
}

55
master.go Normal file
View File

@ -0,0 +1,55 @@
package gobot
import "time"
type Master struct {
Robots []Robot
}
func GobotMaster() *Master {
m := new(Master)
return m
}
func (m *Master) Start() {
for s := range m.Robots {
go m.Robots[s].Start()
}
for {
time.Sleep(10 * time.Millisecond)
}
}
func (m *Master) FindRobot(name string) *Robot {
for s := range m.Robots {
if m.Robots[s].Name == name {
return &m.Robots[s]
}
}
return nil
}
func (m *Master) FindRobotDevice(name string, device string) *Device {
for r := range m.Robots {
if m.Robots[r].Name == name {
for d := range m.Robots[r].devices {
if m.Robots[r].devices[d].Name == device {
return m.Robots[r].devices[d]
}
}
}
}
return nil
}
func (m *Master) FindRobotConnection(name string, connection string) *Connection {
for r := range m.Robots {
if m.Robots[r].Name == name {
for c := range m.Robots[r].connections {
if m.Robots[r].connections[c].Name == connection {
return m.Robots[r].connections[c]
}
}
}
}
return nil
}

View File

@ -11,9 +11,9 @@ type Robot struct {
Connections []interface{}
Devices []interface{}
Name string
Work func()
connections []*Connection
devices []*Device
Work func() `json:"-"`
connections []*Connection `json:"-"`
devices []*Device `json:"-"`
}
func (r *Robot) Start() {
@ -65,3 +65,16 @@ func (r *Robot) startDevices() {
r.devices[i].Start()
}
}
func (r *Robot) GetDevices() []*Device {
return r.devices
}
func (r *Robot) GetDevice(name string) *Device {
for i := range r.devices {
if r.devices[i].Name == name {
return r.devices[i]
}
}
return nil
}

View File

@ -1,8 +1,10 @@
package gobot
import (
"encoding/json"
"math/rand"
"net"
"reflect"
"time"
)
@ -25,9 +27,6 @@ func After(t string, f func()) {
}
func parseDuration(t string) time.Duration {
return ParseDuration(t)
}
func ParseDuration(t string) time.Duration {
dur, err := time.ParseDuration(t)
if err != nil {
panic(err)
@ -35,9 +34,9 @@ func ParseDuration(t string) time.Duration {
return dur
}
func Random(min int, max int) int {
func Rand(max int) int {
rand.Seed(time.Now().UTC().UnixNano())
return rand.Intn(max-min) + min
return rand.Intn(max)
}
func On(cs chan interface{}) interface{} {
@ -47,15 +46,6 @@ func On(cs chan interface{}) interface{} {
return nil
}
func Work(robots []Robot) {
for s := range robots {
go robots[s].Start()
}
for {
time.Sleep(10 * time.Millisecond)
}
}
func ConnectTo(port string) net.Conn {
tcpPort, err := net.Dial("tcp", port)
if err != nil {
@ -63,3 +53,16 @@ func ConnectTo(port string) net.Conn {
}
return tcpPort
}
func Call(thing interface{}, method string, params ...interface{}) []reflect.Value {
in := make([]reflect.Value, len(params))
for k, param := range params {
in[k] = reflect.ValueOf(param)
}
return reflect.ValueOf(thing).MethodByName(method).Call(in)
}
func toJson(obj interface{}) string {
b, _ := json.Marshal(obj)
return string(b)
}