blob: 0c826d7be3a4ddb2b3dc6abae165071f69e21730 [file] [log] [blame]
package main
import (
"log"
"sync"
"v.io/v23"
"v.io/v23/context"
"v.io/v23/discovery"
"v.io/v23/verror"
idiscovery "v.io/x/ref/lib/discovery"
"v.io/x/ref/lib/discovery/plugins/ble"
"v.io/x/ref/lib/discovery/plugins/mdns"
_ "v.io/x/ref/runtime/factories/generic"
mojom "mojom/vanadium/discovery"
"mojo/public/go/application"
"mojo/public/go/bindings"
"mojo/public/go/system"
)
//#include "mojo/public/c/system/types.h"
import "C"
type discoveryService struct {
mu sync.Mutex
ctx *context.T
s discovery.T
trigger *idiscovery.Trigger
nextAdv int32
pendingAdvs map[int32]chan struct{}
nextScan int32
pendingScans map[int32]chan struct{}
}
func convertToErrorStruct(err error) *mojom.Error {
outErr := &mojom.Error{
Id: "v.io/verror/Unknown",
Msg: err.Error(),
}
if e, ok := err.(verror.E); ok {
outErr.Id = string(e.ID)
outErr.Action = int32(e.Action)
}
return outErr
}
func (d *discoveryService) Advertise(s mojom.Service, patterns []string) (int32, *mojom.Error, error) {
vService := discovery.Service{
InstanceUuid: s.InstanceUuid,
InterfaceName: s.InterfaceName,
Attrs: discovery.Attributes(s.Attrs),
Addrs: s.Addrs,
}
ctx, c := context.WithCancel(d.ctx)
err := d.s.Advertise(ctx, vService, nil)
if err != nil {
return 0, convertToErrorStruct(err), nil
}
ch := make(chan struct{})
d.mu.Lock()
id := d.nextAdv
d.pendingAdvs[id] = ch
d.nextAdv++
d.mu.Unlock()
d.trigger.Add(c, ch)
return id, nil, nil
}
func (d *discoveryService) StopAdvertising(handle int32) error {
d.mu.Lock()
ch := d.pendingAdvs[handle]
d.mu.Unlock()
if ch != nil {
close(ch)
}
return nil
}
func vServiceTomService(s discovery.Service) mojom.Service {
return mojom.Service{
InstanceUuid: s.InstanceUuid,
InterfaceName: s.InterfaceName,
Attrs: s.Attrs,
Addrs: s.Addrs,
}
}
func (d *discoveryService) Scan(query string, scanHandler mojom.ScanHandler_Pointer) (int32, *mojom.Error, error) {
ctx, c := context.WithCancel(d.ctx)
proxy := mojom.NewScanHandlerProxy(scanHandler, bindings.GetAsyncWaiter())
scanCh, err := d.s.Scan(ctx, query)
if err != nil {
return 0, convertToErrorStruct(err), nil
}
ch := make(chan struct{})
d.mu.Lock()
id := d.nextScan
d.pendingScans[id] = ch
d.nextScan++
d.mu.Unlock()
d.trigger.Add(c, ch)
go func() {
for v := range scanCh {
switch value := v.(type) {
case discovery.UpdateFound:
proxy.Found(vServiceTomService(value.Value.Service))
case discovery.UpdateLost:
proxy.Lost(vServiceTomService(value.Value.Service))
default:
}
}
}()
return id, nil, nil
}
func (d *discoveryService) StopScan(handle int32) error {
d.mu.Lock()
ch := d.pendingScans[handle]
d.mu.Unlock()
if ch != nil {
close(ch)
}
return nil
}
func (d *discoveryService) stop() {
d.mu.Lock()
for _, ch := range d.pendingScans {
close(ch)
}
for _, ch := range d.pendingAdvs {
close(ch)
}
d.mu.Unlock()
}
type discoveryDelegate struct {
stubs []*bindings.Stub
impl *discoveryService
shutdown v23.Shutdown
}
func (d *discoveryDelegate) Initialize(c application.Context) {
ctx, shutdown := v23.Init()
host := c.Args()[1]
log.Println("Starting discovery with name", host)
mplugin, err := mdns.New(host)
if err != nil {
log.Println("Failed to start mplugin", err)
}
bplugin, err := ble.NewPlugin(host)
if err != nil {
log.Println("Failed to start bplugin", err)
}
d.impl = &discoveryService{
ctx: ctx,
trigger: idiscovery.NewTrigger(),
s: idiscovery.New([]idiscovery.Plugin{mplugin, bplugin}),
pendingAdvs: map[int32]chan struct{}{},
pendingScans: map[int32]chan struct{}{},
}
d.shutdown = shutdown
}
func (d *discoveryDelegate) Create(request mojom.Discoverer_Request) {
stub := mojom.NewDiscovererStub(request, d.impl, bindings.GetAsyncWaiter())
d.stubs = append(d.stubs, stub)
go func() {
for {
if err := stub.ServeRequest(); err != nil {
connectionErr, ok := err.(*bindings.ConnectionError)
if !ok || !connectionErr.Closed() {
log.Println(err)
}
break
}
}
}()
}
func (d *discoveryDelegate) AcceptConnection(connection *application.Connection) {
connection.ProvideServices(&mojom.Discoverer_ServiceFactory{d})
}
func (d *discoveryDelegate) Quit() {
d.impl.stop()
d.shutdown()
for _, stub := range d.stubs {
stub.Close()
}
}
//export MojoMain
func MojoMain(handle C.MojoHandle) C.MojoResult {
application.Run(&discoveryDelegate{}, system.MojoHandle(handle))
return C.MOJO_RESULT_OK
}
func main() {
}