2014-04-27 18:02:39 -07:00
|
|
|
package joystick
|
2014-04-26 03:11:51 -07:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
2014-11-19 15:45:59 -08:00
|
|
|
"time"
|
2014-07-09 18:32:27 -07:00
|
|
|
|
2016-02-20 14:27:24 -08:00
|
|
|
"github.com/veandco/go-sdl2/sdl"
|
2014-07-09 18:32:27 -07:00
|
|
|
"github.com/hybridgroup/gobot"
|
2014-04-26 03:11:51 -07:00
|
|
|
)
|
|
|
|
|
2014-11-22 19:54:32 -08:00
|
|
|
var _ gobot.Driver = (*JoystickDriver)(nil)
|
2014-11-16 12:25:48 -08:00
|
|
|
|
2015-01-03 05:06:08 -08:00
|
|
|
// JoystickDriver represents a joystick
|
2014-04-26 03:11:51 -07:00
|
|
|
type JoystickDriver struct {
|
2014-11-22 19:54:32 -08:00
|
|
|
name string
|
|
|
|
interval time.Duration
|
|
|
|
connection gobot.Connection
|
2014-11-19 15:45:59 -08:00
|
|
|
configPath string
|
|
|
|
config joystickConfig
|
|
|
|
poll func() sdl.Event
|
2014-12-23 01:20:44 -08:00
|
|
|
halt chan bool
|
2014-11-28 18:37:03 -08:00
|
|
|
gobot.Eventer
|
2014-04-26 03:11:51 -07:00
|
|
|
}
|
|
|
|
|
2014-10-20 10:46:03 -05:00
|
|
|
// pair is a JSON representation of name and id
|
2014-04-26 03:11:51 -07:00
|
|
|
type pair struct {
|
|
|
|
Name string `json:"name"`
|
2014-06-10 15:16:11 -07:00
|
|
|
ID int `json:"id"`
|
2014-04-26 03:11:51 -07:00
|
|
|
}
|
|
|
|
|
2014-10-20 10:46:03 -05:00
|
|
|
// hat is a JSON representation of hat, name and id
|
2014-04-26 03:11:51 -07:00
|
|
|
type hat struct {
|
|
|
|
Hat int `json:"hat"`
|
|
|
|
Name string `json:"name"`
|
2014-06-10 15:16:11 -07:00
|
|
|
ID int `json:"id"`
|
2014-04-26 03:11:51 -07:00
|
|
|
}
|
|
|
|
|
2014-10-20 10:46:03 -05:00
|
|
|
// joystickConfig is a JSON representation of configuration values
|
2014-04-26 03:11:51 -07:00
|
|
|
type joystickConfig struct {
|
|
|
|
Name string `json:"name"`
|
2015-01-03 05:06:08 -08:00
|
|
|
GUID string `json:"guid"`
|
2014-04-26 03:11:51 -07:00
|
|
|
Axis []pair `json:"axis"`
|
|
|
|
Buttons []pair `json:"buttons"`
|
|
|
|
Hats []hat `json:"Hats"`
|
|
|
|
}
|
|
|
|
|
2015-01-03 05:06:08 -08:00
|
|
|
// NewJoystickDriver returns a new JoystickDriver with a polling interval of
|
|
|
|
// 10 Milliseconds given a JoystickAdaptor, name and json button configuration
|
|
|
|
// file location.
|
2014-10-20 10:46:03 -05:00
|
|
|
//
|
2015-01-03 05:06:08 -08:00
|
|
|
// Optinally accepts:
|
|
|
|
// time.Duration: Interval at which the JoystickDriver is polled for new information
|
2014-11-28 18:37:03 -08:00
|
|
|
func NewJoystickDriver(a *JoystickAdaptor, name string, config string, v ...time.Duration) *JoystickDriver {
|
2014-04-27 18:02:39 -07:00
|
|
|
d := &JoystickDriver{
|
2014-11-22 19:54:32 -08:00
|
|
|
name: name,
|
|
|
|
connection: a,
|
|
|
|
Eventer: gobot.NewEventer(),
|
2014-11-19 15:45:59 -08:00
|
|
|
configPath: config,
|
2014-07-22 13:55:19 -07:00
|
|
|
poll: func() sdl.Event {
|
|
|
|
return sdl.PollEvent()
|
|
|
|
},
|
2014-11-28 18:37:03 -08:00
|
|
|
interval: 10 * time.Millisecond,
|
2014-12-23 01:20:44 -08:00
|
|
|
halt: make(chan bool, 0),
|
2014-11-28 18:37:03 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(v) > 0 {
|
|
|
|
d.interval = v[0]
|
2014-04-27 18:02:39 -07:00
|
|
|
}
|
2014-04-26 03:11:51 -07:00
|
|
|
|
2014-11-19 15:45:59 -08:00
|
|
|
d.AddEvent("error")
|
2014-04-26 03:11:51 -07:00
|
|
|
return d
|
|
|
|
}
|
2015-01-03 05:06:08 -08:00
|
|
|
|
|
|
|
// Name returns the JoystickDrivers name
|
|
|
|
func (j *JoystickDriver) Name() string { return j.name }
|
|
|
|
|
|
|
|
// Connection returns the JoystickDrivers connection
|
2014-11-22 19:54:32 -08:00
|
|
|
func (j *JoystickDriver) Connection() gobot.Connection { return j.connection }
|
2014-04-26 03:11:51 -07:00
|
|
|
|
2014-10-20 10:46:03 -05:00
|
|
|
// adaptor returns joystick adaptor
|
2014-06-15 17:22:50 -07:00
|
|
|
func (j *JoystickDriver) adaptor() *JoystickAdaptor {
|
2014-11-22 19:54:32 -08:00
|
|
|
return j.Connection().(*JoystickAdaptor)
|
2014-06-15 17:22:50 -07:00
|
|
|
}
|
|
|
|
|
2015-01-03 05:06:08 -08:00
|
|
|
// Start and polls the state of the joystick at the given interval.
|
|
|
|
//
|
|
|
|
// Emits the Events:
|
|
|
|
// Error error - On button error
|
|
|
|
// Events defined in the json button configuration file.
|
|
|
|
// They will have the format:
|
|
|
|
// [button]_press
|
|
|
|
// [button]_release
|
|
|
|
// [axis]
|
2014-11-19 23:21:19 -08:00
|
|
|
func (j *JoystickDriver) Start() (errs []error) {
|
2014-11-19 15:45:59 -08:00
|
|
|
file, err := ioutil.ReadFile(j.configPath)
|
|
|
|
if err != nil {
|
2014-11-19 23:21:19 -08:00
|
|
|
return []error{err}
|
2014-11-19 15:45:59 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
var jsontype joystickConfig
|
|
|
|
json.Unmarshal(file, &jsontype)
|
|
|
|
j.config = jsontype
|
|
|
|
|
|
|
|
for _, value := range j.config.Buttons {
|
|
|
|
j.AddEvent(fmt.Sprintf("%s_press", value.Name))
|
|
|
|
j.AddEvent(fmt.Sprintf("%s_release", value.Name))
|
|
|
|
}
|
|
|
|
for _, value := range j.config.Axis {
|
|
|
|
j.AddEvent(value.Name)
|
|
|
|
}
|
|
|
|
for _, value := range j.config.Hats {
|
|
|
|
j.AddEvent(value.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
for {
|
2016-02-22 04:54:17 +05:30
|
|
|
for event := j.poll(); event != nil; event = j.poll() {
|
2014-11-19 15:45:59 -08:00
|
|
|
if err = j.handleEvent(event); err != nil {
|
|
|
|
gobot.Publish(j.Event("error"), err)
|
|
|
|
}
|
|
|
|
}
|
2014-12-23 01:20:44 -08:00
|
|
|
select {
|
|
|
|
case <-time.After(j.interval):
|
|
|
|
case <-j.halt:
|
|
|
|
return
|
|
|
|
}
|
2014-07-22 13:55:19 -07:00
|
|
|
}
|
2014-11-19 15:45:59 -08:00
|
|
|
}()
|
2014-11-19 23:21:19 -08:00
|
|
|
return
|
2014-07-22 13:55:19 -07:00
|
|
|
}
|
|
|
|
|
2014-11-19 23:21:19 -08:00
|
|
|
// Halt stops joystick driver
|
2014-12-23 01:20:44 -08:00
|
|
|
func (j *JoystickDriver) Halt() (errs []error) {
|
|
|
|
j.halt <- true
|
|
|
|
return
|
|
|
|
}
|
2014-11-19 23:21:19 -08:00
|
|
|
|
2014-10-20 10:46:03 -05:00
|
|
|
// HandleEvent publishes an specific event according to data received
|
2014-07-22 13:55:19 -07:00
|
|
|
func (j *JoystickDriver) handleEvent(event sdl.Event) error {
|
|
|
|
switch data := event.(type) {
|
|
|
|
case *sdl.JoyAxisEvent:
|
|
|
|
if data.Which == j.adaptor().joystick.InstanceID() {
|
|
|
|
axis := j.findName(data.Axis, j.config.Axis)
|
|
|
|
if axis == "" {
|
2015-01-03 05:06:08 -08:00
|
|
|
return fmt.Errorf("Unknown Axis: %v", data.Axis)
|
2014-07-22 13:55:19 -07:00
|
|
|
}
|
2015-01-03 05:06:08 -08:00
|
|
|
gobot.Publish(j.Event(axis), data.Value)
|
2014-07-22 13:55:19 -07:00
|
|
|
}
|
|
|
|
case *sdl.JoyButtonEvent:
|
|
|
|
if data.Which == j.adaptor().joystick.InstanceID() {
|
|
|
|
button := j.findName(data.Button, j.config.Buttons)
|
|
|
|
if button == "" {
|
2015-01-03 05:06:08 -08:00
|
|
|
return fmt.Errorf("Unknown Button: %v", data.Button)
|
|
|
|
}
|
|
|
|
if data.State == 1 {
|
|
|
|
gobot.Publish(j.Event(fmt.Sprintf("%s_press", button)), nil)
|
2014-04-26 03:11:51 -07:00
|
|
|
}
|
2015-01-03 05:06:08 -08:00
|
|
|
gobot.Publish(j.Event(fmt.Sprintf("%s_release", button)), nil)
|
2014-04-26 03:11:51 -07:00
|
|
|
}
|
2014-07-22 13:55:19 -07:00
|
|
|
case *sdl.JoyHatEvent:
|
|
|
|
if data.Which == j.adaptor().joystick.InstanceID() {
|
|
|
|
hat := j.findHatName(data.Value, data.Hat, j.config.Hats)
|
|
|
|
if hat == "" {
|
2015-01-03 05:06:08 -08:00
|
|
|
return fmt.Errorf("Unknown Hat: %v %v", data.Hat, data.Value)
|
2014-07-22 13:55:19 -07:00
|
|
|
}
|
2015-01-03 05:06:08 -08:00
|
|
|
gobot.Publish(j.Event(hat), true)
|
2014-07-22 13:55:19 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2014-04-26 03:11:51 -07:00
|
|
|
}
|
2014-07-22 13:55:19 -07:00
|
|
|
|
2014-04-27 18:02:39 -07:00
|
|
|
func (j *JoystickDriver) findName(id uint8, list []pair) string {
|
2014-04-26 03:11:51 -07:00
|
|
|
for _, value := range list {
|
2014-06-10 15:16:11 -07:00
|
|
|
if int(id) == value.ID {
|
2014-04-26 03:11:51 -07:00
|
|
|
return value.Name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2014-10-20 10:46:03 -05:00
|
|
|
// findHatName returns name from hat found by id in provided list
|
2014-04-27 18:02:39 -07:00
|
|
|
func (j *JoystickDriver) findHatName(id uint8, hat uint8, list []hat) string {
|
2014-10-20 10:46:03 -05:00
|
|
|
for _, lHat := range list {
|
|
|
|
if int(id) == lHat.ID && int(hat) == lHat.Hat {
|
|
|
|
return lHat.Name
|
2014-04-26 03:11:51 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|