Upgrade to Pro — share decks privately, control downloads, hide ads and more …

A Droid's Journey - RubyKaigi 2018

hone
June 01, 2018

A Droid's Journey - RubyKaigi 2018

Joint talk with @code0100fun

Intro here: https://dee-too-intro.herokuapp.com/

Ruby has never been at the forefront of dealing with robots, IoT, or other low level systems. What Ruby is great at is scripting and building DSLs. Using mruby we can leverage existing ecosystems while still using the language we love.

In this talk, we'll deep dive into how we can execute mruby handlers inside a Go event reactor to control a Sphero R2-D2. With surprisingly few lines of code, you can coordinate motors, lights, and sound concurrently. Come learn about mruby & robotics and see the Droids™ you're looking for in action.

hone

June 01, 2018
Tweet

More Decks by hone

Other Decks in Programming

Transcript

  1. Ruby Kaigi Episode XII It is a dark time for

    the galaxy. The new GO ORDER has seized power over the systems, imposing it’s tyrannical rules on the population. Space battles are being fought across the galaxy and the TAB fighters are winning. The ORDER has been busy converting historic libraries, compiling their scripts into new libraries that will bend to the will of GO. With the support of a large group of fanatic and loyal followers it seems all other languages will be lost to the might of the ORDER. But a small RUBY ALLIANCE has plans to hijack the ORDER’s Goroutines and in doing so, embed itself into their droid network. A difficult task lies ahead... A surprisingly short time ago, when this conference was not too far away...
  2. • Sphero support* • *Only Bluetooth Classic • No support

    for BLE! • No BB8! GitHub issue #165 Artoo
  3. BB8 Hello World func main() { bleAdaptor := ble.NewClientAdaptor(os.Args[1]) bb8

    := bb8.NewDriver(bleAdaptor) work := func() { gobot.Every(1*time.Second, func() { r := uint8(gobot.Rand(255)) g := uint8(gobot.Rand(255)) b := uint8(gobot.Rand(255)) bb8.SetRGB(r, g, b) }) } robot := gobot.NewRobot("bb", []gobot.Connection{bleAdaptor}, []gobot.Device{bb8}, work, ) robot.Start() }
  4. gobot.NewRobot() work := func() { gobot.Every(1*time.Second, func() { r :=

    uint8(gobot.Rand(255)) g := uint8(gobot.Rand(255)) b := uint8(gobot.Rand(255)) bb8.SetRGB(r, g, b) }) } robot := gobot.NewRobot("bb", []gobot.Connection{bleAdaptor}, []gobot.Device{bb8}, work, ) robot.Start()
  5. Terminology • BLE - Bluetooth Low Energy ◦ Modern Bluetooth

    4.* spec • GATT - Generic Attribute ◦ Like a BLE plugin system
  6. Services • Encapsulate the behavior of part of a device

    • Collection of data and associated behaviors
  7. gatttool $ sudo hcitool lescan LE Scan ... DF:59:60:07:1F:3B D2-1F3B

    $ gatttool -b DF:59:60:07:1F:3B -I [DF:59:60:07:1F:3B][LE]> connect Attempting to connect to DF:59:60:07:1F:3B Connection successful
  8. gatttool $ sudo hcitool lescan LE Scan ... DF:59:60:07:1F:3B D2-1F3B

    $ gatttool -b DF:59:60:07:1F:3B -I [DF:59:60:07:1F:3B][LE]> connect Attempting to connect to DF:59:60:07:1F:3B Connection successful [DF:59:60:07:1F:3B][LE]> (gatttool:11398): GLib-WARNING **: Invalid file descriptor.
  9. • Reset logfiles • Send a command from the app

    • Dump logfiles • Load and find command • Record value • Repeat • Repeat again • ...and again Extracting Commands
  10. Look for patterns Tripod - 8d:0a:17:0d:01:01:cf:d8 Bipod - 8d:0a:17:0d:02:02:cd:d8 Tripod

    - 8d:0a:17:0d:03:01:cd:d8 Bipod - 8d:0a:17:0d:04:02:cb:d8 Tripod - 8d:0a:17:0d:05:01:cb:d8
  11. Look for patterns Tripod - 8d:0a:17:0d:01:01:cf:d8 Bipod - 8d:0a:17:0d:02:02:cd:d8 Tripod

    - 8d:0a:17:0d:03:01:cd:d8 Bipod - 8d:0a:17:0d:04:02:cb:d8 Tripod - 8d:0a:17:0d:05:01:cb:d8 Same for all
  12. Look for patterns Tripod - 8d:0a:17:0d:01:01:cf:d8 Bipod - 8d:0a:17:0d:02:02:cd:d8 Tripod

    - 8d:0a:17:0d:03:01:cd:d8 Bipod - 8d:0a:17:0d:04:02:cb:d8 Tripod - 8d:0a:17:0d:05:01:cb:d8 Changing
  13. Look for patterns Tripod - 8d:0a:17:0d:01:01:cf:d8 Bipod - 8d:0a:17:0d:02:02:cd:d8 Tripod

    - 8d:0a:17:0d:03:01:cd:d8 Bipod - 8d:0a:17:0d:04:02:cb:d8 Tripod - 8d:0a:17:0d:05:01:cb:d8 Sequence
  14. Look for patterns Tripod - 8d:0a:17:0d:01:01:cf:d8 Bipod - 8d:0a:17:0d:02:02:cd:d8 Tripod

    - 8d:0a:17:0d:03:01:cd:d8 Bipod - 8d:0a:17:0d:04:02:cb:d8 Tripod - 8d:0a:17:0d:05:01:cb:d8 Command Value
  15. Look for patterns Tripod - 8d:0a:17:0d:01:01:cf:d8 Bipod - 8d:0a:17:0d:02:02:cd:d8 Tripod

    - 8d:0a:17:0d:03:01:cd:d8 Bipod - 8d:0a:17:0d:04:02:cb:d8 Tripod - 8d:0a:17:0d:05:01:cb:d8 Checksum ~((0x0a + 0x17 + 0x0d + 0x05 + 0x01) % 0xFF) = 0xcb
  16. R2D2 Driver package r2d2 type Driver struct { name string

    connection gobot.Connection seq uint8 mtx sync.Mutex collisionResponse []uint8 packetChannel chan *packet gobot.Eventer } func (b *Driver) Tripod() { log.Print("Tripod") b.packetChannel <- b.craftPacket(0x17, 0x0d, []uint8{0x01}) }
  17. func (b *Driver) Tripod() { log.Print("Tripod") b.packetChannel <- b.craftPacket(0x17, 0x0d,

    []uint8{0x01}) } func (b *Driver) craftPacket(did byte, cid byte, body []uint8) *packet { b.mtx.Lock() defer b.mtx.Unlock() packet := new(packet) packet.header = []uint8{0x8d, 0x0a, did, cid, b.seq} packet.body = body packet.checksum = b.calculateChecksum(packet) packet.footer = []uint8{0xd8} return packet } craftPacket 8d:0a:17:0d:01:01:cf:d8
  18. go-mruby Hello World func main() { mrb := mruby.NewMrb() defer

    mrb.Close() addFunc := func(m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) { args := m.GetArgs() return mruby.Int(args[0].Fixnum() + args[1].Fixnum()), nil } class := mrb.DefineClass("Example", nil) class.DefineClassMethod("add", addFunc, mruby.ArgsReq(2)) result, err := mrb.LoadString(`Example.add(12, 30)`) if err != nil { panic(err.Error()) } fmt.Printf("Result: %s\n", result.String()) }
  19. go-mruby Hello World func main() { mrb := mruby.NewMrb() defer

    mrb.Close() addFunc := func(m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) { args := m.GetArgs() return mruby.Int(args[0].Fixnum() + args[1].Fixnum()), nil } class := mrb.DefineClass("Example", nil) class.DefineClassMethod("add", addFunc, mruby.ArgsReq(2)) result, err := mrb.LoadString(`Example.add(12, 30)`) if err != nil { panic(err.Error()) } fmt.Printf("Result: %s\n", result.String()) }
  20. go-mruby Hello World func main() { mrb := mruby.NewMrb() defer

    mrb.Close() addFunc := func(m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) { args := m.GetArgs() return mruby.Int(args[0].Fixnum() + args[1].Fixnum()), nil } class := mrb.DefineClass("Example", nil) class.DefineClassMethod("add", addFunc, mruby.ArgsReq(2)) result, err := mrb.LoadString(`Example.add(12, 30)`) if err != nil { panic(err.Error()) } fmt.Printf("Result: %s\n", result.String()) }
  21. go-mruby Hello World func main() { mrb := mruby.NewMrb() defer

    mrb.Close() addFunc := func(m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) { args := m.GetArgs() return mruby.Int(args[0].Fixnum() + args[1].Fixnum()), nil } class := mrb.DefineClass("Example", nil) class.DefineClassMethod("add", addFunc, mruby.ArgsReq(2)) result, err := mrb.LoadString(`Example.add(12, 30)`) if err != nil { panic(err.Error()) } fmt.Printf("Result: %s\n", result.String()) }
  22. go-mruby Hello World func main() { mrb := mruby.NewMrb() defer

    mrb.Close() addFunc := func(m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) { args := m.GetArgs() return mruby.Int(args[0].Fixnum() + args[1].Fixnum()), nil } class := mrb.DefineClass("Example", nil) class.DefineClassMethod("add", addFunc, mruby.ArgsReq(2)) result, err := mrb.LoadString(`Example.add(12, 30)`) if err != nil { panic(err.Error()) } fmt.Printf("Result: %s\n", result.String()) }
  23. go-mruby Hello World func main() { mrb := mruby.NewMrb() defer

    mrb.Close() addFunc := func(m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) { args := m.GetArgs() return mruby.Int(args[0].Fixnum() + args[1].Fixnum()), nil } class := mrb.DefineClass("Example", nil) class.DefineClassMethod("add", addFunc, mruby.ArgsReq(2)) result, err := mrb.LoadString(`Example.add(12, 30)`) if err != nil { panic(err.Error()) } fmt.Printf("Result: %s\n", result.String()) }
  24. go-mruby Hello World func main() { mrb := mruby.NewMrb() defer

    mrb.Close() addFunc := func(m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) { args := m.GetArgs() return mruby.Int(args[0].Fixnum() + args[1].Fixnum()), nil } class := mrb.DefineClass("Example", nil) class.DefineClassMethod("add", addFunc, mruby.ArgsReq(2)) result, err := mrb.LoadString(`Example.add(12, 30)`) if err != nil { panic(err.Error()) } fmt.Printf("Result: %s\n", result.String()) }
  25. go-mruby Hello World func main() { mrb := mruby.NewMrb() defer

    mrb.Close() addFunc := func(m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) { args := m.GetArgs() return mruby.Int(args[0].Fixnum() + args[1].Fixnum()), nil } class := mrb.DefineClass("Example", nil) class.DefineClassMethod("add", addFunc, mruby.ArgsReq(2)) result, err := mrb.LoadString(`Example.add(12, 30)`) if err != nil { panic(err.Error()) } fmt.Printf("Result: %s\n", result.String()) }
  26. go-mruby Hello World func main() { mrb := mruby.NewMrb() defer

    mrb.Close() addFunc := func(m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) { args := m.GetArgs() return mruby.Int(args[0].Fixnum() + args[1].Fixnum()), nil } class := mrb.DefineClass("Example", nil) class.DefineClassMethod("add", addFunc, mruby.ArgsReq(2)) result, err := mrb.LoadString(`Example.add(12, 30)`) if err != nil { panic(err.Error()) } fmt.Printf("Result: %s\n", result.String()) }
  27. 1. go-mruby: Builds libmruby.a - MRUBY_CONFIG allows us to specify

    our own build_config.rb Build Process MRuby::Build.new do |conf| toolchain :gcc conf.gem :git => 'https://github.com/matsumoto-r/mruby-sleep.git' conf.gem :core => 'mruby-random' conf.gembox 'default' end
  28. 1. go-mruby: Builds libmruby.a - MRUBY_CONFIG allows us to specify

    our own build_config.rb - vendor mruby: clones mruby from github - build mruby - copy libmruby.a out Build Process
  29. 1. go-mruby: Builds libmruby.a - MRUBY_CONFIG allows us to specify

    our own build_config.rb - vendor mruby: clones mruby from github - build mruby - copy libmruby.a out 2. `go build` links against libmruby.a using Cgo Build Process // #cgo CFLAGS: -Ivendor/mruby/include // #cgo LDFLAGS: ${SRCDIR}/libmruby.a -lm import "C"
  30. Vision • Only need to write Ruby • Extensible (leverage

    existing work) • Coordinate multiple async tasks • Small??
  31. r2d2_dome.mrb droid = R2D2.new("D2-1F3B") heading = 0; direction = -1

    while true do droid.dome(heading) direction = -1 if heading >= 180 direction = 1 if heading <= -160 heading = heading + direction Sleep::usleep(100) end
  32. r2d2_dome.mrb droid = R2D2.new("D2-1F3B") heading = 0; direction = -1

    while true do droid.dome(heading) direction = -1 if heading >= 180 direction = 1 if heading <= -160 heading = heading + direction Sleep::usleep(100) end
  33. r2d2_dome.mrb droid = R2D2.new("D2-1F3B") heading = 0; direction = -1

    while true do droid.dome(heading) direction = -1 if heading >= 180 direction = 1 if heading <= -160 heading = heading + direction Sleep::usleep(100) end
  34. startWorker() func main() { var wg sync.WaitGroup table := NewDroidTable()

    argsWithoutProg := os.Args[1:] for _, mrbFile := range argsWithoutProg { startWorker(mrbFile, table, &wg) } c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c for _ = range argsWithoutProg { wg.Done() } }() wg.Wait() }
  35. startWorker() func main() { var wg sync.WaitGroup table := NewDroidTable()

    argsWithoutProg := os.Args[1:] for _, mrbFile := range argsWithoutProg { startWorker(mrbFile, table, &wg) } c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c for _ = range argsWithoutProg { wg.Done() } }() wg.Wait() }
  36. startWorker() func main() { var wg sync.WaitGroup table := NewDroidTable()

    argsWithoutProg := os.Args[1:] for _, mrbFile := range argsWithoutProg { startWorker(mrbFile, table, &wg) } c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c for _ = range argsWithoutProg { wg.Done() } }() wg.Wait() }
  37. startWorker() func startWorker(file string, table droidTable, wg *sync.WaitGroup) { wg.Add(1)

    go func() { mrb := mruby.NewMrb() defer mrb.Close() droids.NewR2D2(table.r2d2, mrb) droids.NewBB8(table.bb8, mrb) dat, err := ioutil.ReadFile(file) content := string(dat) result, err := mrb.LoadString(content) fmt.Printf("Done: %s\n", result) }() }
  38. startWorker() func startWorker(file string, table droidTable, wg *sync.WaitGroup) { wg.Add(1)

    go func() { mrb := mruby.NewMrb() defer mrb.Close() droids.NewR2D2(table.r2d2, mrb) droids.NewBB8(table.bb8, mrb) dat, err := ioutil.ReadFile(file) content := string(dat) result, err := mrb.LoadString(content) fmt.Printf("Done: %s\n", result) }() }
  39. startWorker() func startWorker(file string, table droidTable, wg *sync.WaitGroup) { wg.Add(1)

    go func() { mrb := mruby.NewMrb() defer mrb.Close() droids.NewR2D2(table.r2d2, mrb) droids.NewBB8(table.bb8, mrb) dat, err := ioutil.ReadFile(file) content := string(dat) result, err := mrb.LoadString(content) fmt.Printf("Done: %s\n", result) }() }
  40. startWorker() func startWorker(file string, table droidTable, wg *sync.WaitGroup) { wg.Add(1)

    go func() { mrb := mruby.NewMrb() defer mrb.Close() droids.NewR2D2(table.r2d2, mrb) droids.NewBB8(table.bb8, mrb) dat, err := ioutil.ReadFile(file) content := string(dat) result, err := mrb.LoadString(content) fmt.Printf("Done: %s\n", result) }() }
  41. Define mruby Classes func startWorker(file string, table droidTable, wg *sync.WaitGroup)

    { wg.Add(1) go func() { mrb := mruby.NewMrb() defer mrb.Close() droids.NewR2D2(table.r2d2, mrb) droids.NewBB8(table.bb8, mrb) dat, err := ioutil.ReadFile(file) content := string(dat) result, err := mrb.LoadString(content) fmt.Printf("Done: %s\n", result) }() }
  42. Define mruby Classes func NewR2D2(table map[string]*gobotR2D2.Driver, mrb *mruby.Mrb) *r2d2 {

    droids := r2d2{ table: table, } class := mrb.DefineClass("R2D2", nil) _, err := mrb.LoadString(` class R2D2 attr_accessor :name end `) class.DefineMethod("initialize", droids.Initialize, mruby.ArgsReq(1)) class.DefineMethod("dome", droids.Dome, mruby.ArgsReq(1)) class.DefineMethod("tripod", droids.Tripod, mruby.ArgsReq(0)) class.DefineMethod("bipod", droids.Bipod, mruby.ArgsReq(0)) class.DefineMethod("macro", droids.Macro, mruby.ArgsReq(1)) class.DefineMethod("move", droids.Move, mruby.ArgsReq(2)) return &droids }
  43. Define mruby Classes func NewR2D2(table map[string]*gobotR2D2.Driver, mrb *mruby.Mrb) *r2d2 {

    droids := r2d2{ table: table, } class := mrb.DefineClass("R2D2", nil) _, err := mrb.LoadString(` class R2D2 attr_accessor :name end `) class.DefineMethod("initialize", droids.Initialize, mruby.ArgsReq(1)) class.DefineMethod("dome", droids.Dome, mruby.ArgsReq(1)) class.DefineMethod("tripod", droids.Tripod, mruby.ArgsReq(0)) class.DefineMethod("bipod", droids.Bipod, mruby.ArgsReq(0)) class.DefineMethod("macro", droids.Macro, mruby.ArgsReq(1)) class.DefineMethod("move", droids.Move, mruby.ArgsReq(2)) return &droids }
  44. startWorker func startWorker(file string, table droidTable, wg *sync.WaitGroup) { wg.Add(1)

    go func() { mrb := mruby.NewMrb() defer mrb.Close() droids.NewR2D2(table.r2d2, mrb) droids.NewBB8(table.bb8, mrb) dat, err := ioutil.ReadFile(file) content := string(dat) result, err := mrb.LoadString(content) fmt.Printf("Done: %s\n", result) }() }
  45. R2D2.new droid = R2D2.new("D2-1F3B") heading = 0 direction = -1

    while true do droid.dome(heading) direction = -1 if heading >= 180 direction = 1 if heading <= -160 heading = heading + direction Sleep::usleep(100) end
  46. R2D2.new func NewR2D2(table map[string]*gobotR2D2.Driver, mrb *mruby.Mrb) *r2d2 { droids :=

    r2d2{ table: table, } class := mrb.DefineClass("R2D2", nil) _, err := mrb.LoadString(` class R2D2 attr_accessor :name end `) class.DefineMethod("initialize", droids.Initialize, mruby.ArgsReq(1)) class.DefineMethod("dome", droids.Dome, mruby.ArgsReq(1)) class.DefineMethod("tripod", droids.Tripod, mruby.ArgsReq(0)) class.DefineMethod("bipod", droids.Bipod, mruby.ArgsReq(0)) class.DefineMethod("macro", droids.Macro, mruby.ArgsReq(1)) class.DefineMethod("move", droids.Move, mruby.ArgsReq(2)) return &droids }
  47. R2D2.new func (droids *r2d2) Initialize(mrb *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value)

    { args := mrb.GetArgs() name := string(args[0].String()) bleAdaptor := ble.NewClientAdaptor(name) droidDriver := gobotR2D2.NewDriver(bleAdaptor) droids.table[name] = droidDriver self.Call("name=", args[0]) var wg sync.WaitGroup wg.Add(1) work := func() { wg.Done() } robot := gobot.NewRobot("R2D2", []gobot.Connection{bleAdaptor}, []gobot.Device{droidDriver}, work) go robot.Start() wg.Wait() return self, nil }
  48. R2D2.new func (droids *r2d2) Initialize(mrb *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value)

    { args := mrb.GetArgs() name := string(args[0].String()) bleAdaptor := ble.NewClientAdaptor(name) droidDriver := gobotR2D2.NewDriver(bleAdaptor) droids.table[name] = droidDriver self.Call("name=", args[0]) var wg sync.WaitGroup wg.Add(1) work := func() { wg.Done() } robot := gobot.NewRobot("R2D2", []gobot.Connection{bleAdaptor}, []gobot.Device{droidDriver}, work) go robot.Start() wg.Wait() return self, nil }
  49. R2D2.new func (droids *r2d2) Initialize(mrb *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value)

    { args := mrb.GetArgs() name := string(args[0].String()) bleAdaptor := ble.NewClientAdaptor(name) droidDriver := gobotR2D2.NewDriver(bleAdaptor) droids.table[name] = droidDriver self.Call("name=", args[0]) var wg sync.WaitGroup wg.Add(1) work := func() { wg.Done() } robot := gobot.NewRobot("R2D2", []gobot.Connection{bleAdaptor}, []gobot.Device{droidDriver}, work) go robot.Start() wg.Wait() return self, nil }
  50. dome() in mruby droid = R2D2.new("D2-1F3B") heading = 0; direction

    = -1 while true do droid.dome(heading) direction = -1 if heading >= 180 direction = 1 if heading <= -160 heading = heading + direction Sleep::usleep(100) end
  51. dome() in mruby func NewR2D2(table map[string]*gobotR2D2.Driver, mrb *mruby.Mrb) *r2d2 {

    droids := r2d2{ table: table, } class := mrb.DefineClass("R2D2", nil) _, err := mrb.LoadString(` class R2D2 attr_accessor :name end `) class.DefineMethod("initialize", droids.Initialize, mruby.ArgsReq(1)) class.DefineMethod("dome", droids.Dome, mruby.ArgsReq(1)) class.DefineMethod("tripod", droids.Tripod, mruby.ArgsReq(0)) class.DefineMethod("bipod", droids.Bipod, mruby.ArgsReq(0)) class.DefineMethod("macro", droids.Macro, mruby.ArgsReq(1)) class.DefineMethod("move", droids.Move, mruby.ArgsReq(2)) return &droids }
  52. Dome() in DeeToo func (droids *r2d2) Dome(mrb *mruby.Mrb, self *mruby.MrbValue)

    (mruby.Value, mruby.Value) { args := mrb.GetArgs() heading := int16(args[0].Fixnum()) droids.driver(self).Dome(heading) return nil, nil }
  53. Dome() in DeeToo func (droids *r2d2) Dome(mrb *mruby.Mrb, self *mruby.MrbValue)

    (mruby.Value, mruby.Value) { args := mrb.GetArgs() heading := int16(args[0].Fixnum()) droids.driver(self).Dome(heading) return nil, nil }
  54. Dome() in gobot R2D2 Driver func (b *Driver) Dome(heading int16)

    { log.Printf("Dome - heading: %v", heading) encoded,_ := EncodeSixShifted(heading) b.packetChannel <- b.craftPacket(0x17, 0x0f, append(encoded, 0x00, 0x00)) }
  55. Droid Table func main() { var wg sync.WaitGroup table :=

    NewDroidTable() argsWithoutProg := os.Args[1:] for _, mrbFile := range argsWithoutProg { startWorker(mrbFile, table, &wg) } c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c for _ = range argsWithoutProg { wg.Done() } }() wg.Wait() }
  56. Droid Table func main() { var wg sync.WaitGroup table :=

    NewDroidTable() argsWithoutProg := os.Args[1:] for _, mrbFile := range argsWithoutProg { startWorker(mrbFile, table, &wg) } c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c for _ = range argsWithoutProg { wg.Done() } }() wg.Wait() }
  57. Droid Table type droidTable struct { bb8 map[string]*bb8.BB8Driver r2d2 map[string]*r2d2.Driver

    } func NewDroidTable() droidTable { table := droidTable{ bb8: make(map[string]*bb8.BB8Driver), r2d2: make(map[string]*r2d2.Driver), } return table }
  58. Droid Table func main() { var wg sync.WaitGroup table :=

    NewDroidTable() argsWithoutProg := os.Args[1:] for _, mrbFile := range argsWithoutProg { startWorker(mrbFile, table, &wg) } c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c for _ = range argsWithoutProg { wg.Done() } }() wg.Wait() }
  59. Droid Table func startWorker(file string, table droidTable, wg *sync.WaitGroup) {

    wg.Add(1) go func() { mrb := mruby.NewMrb() defer mrb.Close() droids.NewR2D2(table.r2d2, mrb) droids.NewBB8(table.bb8, mrb) dat, err := ioutil.ReadFile(file) content := string(dat) result, err := mrb.LoadString(content) fmt.Printf("Done: %s\n", result) }() }
  60. Droid Table func (droids *r2d2) Initialize(mrb *mruby.Mrb, self *mruby.MrbValue) (mruby.Value,

    mruby.Value) { args := mrb.GetArgs() name := string(args[0].String()) bleAdaptor := ble.NewClientAdaptor(name) droidDriver := gobotR2D2.NewDriver(bleAdaptor) droids.table[name] = droidDriver self.Call("name=", args[0]) var wg sync.WaitGroup wg.Add(1) work := func() { wg.Done() } robot := gobot.NewRobot("R2D2", []gobot.Connection{bleAdaptor}, []gobot.Device{droidDriver}, work) go robot.Start() wg.Wait() return self, nil }
  61. Droid Table func NewR2D2(table map[string]*gobotR2D2.Driver, mrb *mruby.Mrb) *r2d2 { droids

    := r2d2{ table: table, } class := mrb.DefineClass("R2D2", nil) _, err := mrb.LoadString(` class R2D2 attr_accessor :name end `) class.DefineMethod("initialize", droids.Initialize, mruby.ArgsReq(1)) class.DefineMethod("dome", droids.Dome, mruby.ArgsReq(1)) class.DefineMethod("tripod", droids.Tripod, mruby.ArgsReq(0)) class.DefineMethod("bipod", droids.Bipod, mruby.ArgsReq(0)) class.DefineMethod("macro", droids.Macro, mruby.ArgsReq(1)) class.DefineMethod("move", droids.Move, mruby.ArgsReq(2)) return &droids }
  62. Droid Table func (droids *r2d2) driver(mrbInstance *mruby.MrbValue) *gobotR2D2.Driver { value,

    err := mrbInstance.Call("name") var driver *gobotR2D2.Driver if err != nil { panic(err.Error()) } else { ident := string(value.String()) driver = droids.table[ident] } return driver }
  63. Droid Table func (droids *r2d2) driver(mrbInstance *mruby.MrbValue) *gobotR2D2.Driver { value,

    err := mrbInstance.Call("name") var driver *gobotR2D2.Driver if err != nil { panic(err.Error()) } else { ident := string(value.String()) driver = droids.table[ident] } return driver }
  64. Future/Roadmap • Single binary by compiling the mruby byte code

    into libmruby.a • Cross compiling the deetoo tool • Message passing between mrb files