247 lines
4.7 KiB
Go
247 lines
4.7 KiB
Go
package ble
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"git.wow.st/gmp/ble/gatt"
|
|
)
|
|
|
|
type PeripheralListItem struct {
|
|
p Peripheral
|
|
seen time.Time
|
|
}
|
|
|
|
type Peripherals struct {
|
|
items []PeripheralListItem
|
|
sync.Mutex
|
|
}
|
|
|
|
type ConnectionListItem struct {
|
|
p Peripheral
|
|
state chan string
|
|
}
|
|
|
|
type Connections struct {
|
|
items []ConnectionListItem
|
|
close chan struct{}
|
|
sync.Mutex
|
|
}
|
|
|
|
func (c *Connections) UpdateState(p Peripheral, s string) {
|
|
var ch chan string
|
|
c.Lock()
|
|
for _, item := range c.items {
|
|
if p.Identifier == item.p.Identifier {
|
|
ch = item.state
|
|
}
|
|
}
|
|
c.Unlock()
|
|
ch <- s
|
|
}
|
|
|
|
func (ps *Peripherals) Add(x Peripheral) bool {
|
|
ps.Lock()
|
|
defer ps.Unlock()
|
|
found := false
|
|
for n, item := range ps.items {
|
|
if item.p.Identifier == x.Identifier {
|
|
item.p = x
|
|
item.seen = time.Now()
|
|
ps.items[n] = item
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if found {
|
|
return false
|
|
}
|
|
//take ownership of this object
|
|
x.Retain()
|
|
item := PeripheralListItem{p: x, seen: time.Now()}
|
|
ps.items = append(ps.items, item)
|
|
return true
|
|
}
|
|
|
|
func (cs *Connections) Add(x ConnectionListItem) bool {
|
|
cs.Lock()
|
|
defer cs.Unlock()
|
|
found := false
|
|
for _, item := range cs.items {
|
|
if item.p.Identifier == x.p.Identifier {
|
|
// are we allowed to have multiple connections to the same peripheral?
|
|
return false
|
|
}
|
|
}
|
|
if found {
|
|
return false
|
|
}
|
|
cs.items = append(cs.items, x)
|
|
return true
|
|
}
|
|
|
|
type UpdateStateEvent struct {
|
|
State string
|
|
}
|
|
|
|
type DiscoverPeripheralEvent struct {
|
|
Peripheral Peripheral
|
|
}
|
|
|
|
//FIXME: create a Service type that is platform dependent to make the
|
|
//event platform independent
|
|
type DiscoverServiceEvent struct {
|
|
Peripheral Peripheral
|
|
Gatt gatt.Service
|
|
Service Service
|
|
}
|
|
|
|
//FIXME: create a Characteristic type that is platform dependent to make the
|
|
//event platform independent
|
|
type DiscoverCharacteristicEvent struct {
|
|
Peripheral Peripheral
|
|
Gatt gatt.Characteristic
|
|
Service Service
|
|
Characteristic Characteristic
|
|
}
|
|
|
|
//FIXME: create a Characteristic type that is platform dependent to make the
|
|
//event platform independent
|
|
type UpdateValueEvent struct {
|
|
Peripheral Peripheral
|
|
Characteristic Characteristic
|
|
Data []byte
|
|
}
|
|
|
|
type ConnectEvent struct {
|
|
Peripheral Peripheral
|
|
}
|
|
type ConnectTimeoutEvent struct {
|
|
Peripheral Peripheral
|
|
}
|
|
|
|
type UpdageValueEvent struct {
|
|
Peripheral Peripheral
|
|
Characteristic gatt.Characteristic
|
|
}
|
|
|
|
func (b *BLE) Events() chan interface{} {
|
|
return b.events
|
|
}
|
|
|
|
func (b *BLE) State() string {
|
|
b.Lock()
|
|
defer b.Unlock()
|
|
return b.stringState()
|
|
}
|
|
|
|
func (b *BLE) Scan() {
|
|
b.Lock()
|
|
defer b.Unlock()
|
|
if b.readyToScan() {
|
|
b.peripherals.Lock()
|
|
b.peripherals.items = b.peripherals.items[:0]
|
|
b.peripherals.Unlock()
|
|
fmt.Printf("Go: Scanning\n")
|
|
b.scan()
|
|
} else {
|
|
b.wantScan = true
|
|
}
|
|
}
|
|
|
|
func (b *BLE) StopScan() {
|
|
b.Lock()
|
|
if !b.ready {
|
|
b.Unlock()
|
|
return
|
|
}
|
|
b.Unlock()
|
|
fmt.Printf("Go: stopping scan\n")
|
|
b.stopScan()
|
|
}
|
|
|
|
func connectTracker(b *BLE, x ConnectionListItem) bool {
|
|
fmt.Printf("connectTracker(): %s\n", x.p.Name)
|
|
b.connections.Lock()
|
|
for _, item := range b.connections.items {
|
|
if item.p.Identifier == x.p.Identifier {
|
|
fmt.Printf("connectTracker(): already connecting to %s\n", x.p.Name)
|
|
b.connections.Unlock()
|
|
return true
|
|
}
|
|
}
|
|
b.connections.items = append(b.connections.items, x)
|
|
b.connections.Unlock()
|
|
b.connectPeripheral(x.p)
|
|
|
|
cancel := func() {
|
|
b.cancelConnection(x.p)
|
|
b.connections.Lock()
|
|
for n, item := range b.connections.items {
|
|
if item.p.Identifier == x.p.Identifier {
|
|
b.connections.items = append(b.connections.items[:n], b.connections.items[n+1:]...)
|
|
}
|
|
}
|
|
b.connections.Unlock()
|
|
}
|
|
|
|
go func() {
|
|
tick := time.NewTicker(time.Second * 5)
|
|
select {
|
|
case <-b.connections.close:
|
|
fmt.Printf("connectTracker(): Closing connection to %s\n", x.p.Name)
|
|
b.cancelConnection(x.p)
|
|
case <-tick.C:
|
|
fmt.Printf("connectTracker(): Connection to %s timed out\n", x.p.Name)
|
|
cancel()
|
|
b.events <- ConnectTimeoutEvent{x.p}
|
|
case state := <-x.state:
|
|
if state == "cancel" {
|
|
cancel()
|
|
}
|
|
fmt.Printf("connectTracker(): state %s\n", state)
|
|
}
|
|
}()
|
|
return true
|
|
}
|
|
|
|
func (b *BLE) Connect(p Peripheral) bool {
|
|
b.Lock()
|
|
if !b.ready {
|
|
b.Unlock()
|
|
fmt.Printf("--BLE not ready\n")
|
|
return false
|
|
}
|
|
b.Unlock()
|
|
if p.p == nil {
|
|
var ok bool
|
|
p, ok = b.knownPeripheral(p)
|
|
if !ok {
|
|
return false
|
|
}
|
|
}
|
|
item := ConnectionListItem{p, make(chan string)}
|
|
fmt.Printf("BLE.Connect() calling connectTracker\n")
|
|
return connectTracker(b, item)
|
|
}
|
|
|
|
func Disconnect(p Peripheral) {
|
|
b := peripheralLookup(p)
|
|
found := false
|
|
b.connections.Lock()
|
|
for i, item := range b.connections.items {
|
|
if item.p.Identifier == p.Identifier {
|
|
found = true
|
|
b.connections.items = append(b.connections.items[:i], b.connections.items[i+1:]...)
|
|
break
|
|
}
|
|
}
|
|
b.connections.Unlock()
|
|
if !found {
|
|
return
|
|
}
|
|
b.cancelConnection(p)
|
|
}
|
|
|