예제 #1
0
        public override IObservable <CharacteristicGattResult> WriteWithoutResponse(byte[] value)
        => this.context.Invoke(Observable.Create <CharacteristicGattResult>(ob =>
        {
            this.AssertWrite(false);

            this.context.InvokeOnMainThread(() =>
            {
                try
                {
                    this.native.WriteType = GattWriteType.NoResponse;

                    if (!this.native.SetValue(value))
                    {
                        throw new BleException("Failed to write characteristic value");
                    }

                    if (!this.context.Gatt.WriteCharacteristic(this.native))
                    {
                        throw new BleException("Failed to write to characteristic");
                    }

                    ob.Respond(new CharacteristicGattResult(this, value));
                }
                catch (Exception ex)
                {
                    throw new BleException("Error during charactersitic write", ex);
                }
            });

            return(Disposable.Empty);
        }));
예제 #2
0
        public override IObservable <CharacteristicResult> WhenNotificationReceived()
        {
            this.AssertNotify();

            this.notifyOb = this.notifyOb ?? Observable.Create <CharacteristicResult>(ob =>
                                                                                      this.context
                                                                                      .Callbacks
                                                                                      .CharacteristicChanged
                                                                                      .Where(this.NativeEquals)
                                                                                      .Subscribe(args =>
            {
                if (!args.IsSuccessful)
                {
                    ob.OnError(new ArgumentException("Error subscribing to " + args.Characteristic.Uuid));
                }
                else
                {
                    this.Value = args.Characteristic.GetValue();

                    var result = new CharacteristicResult(this, CharacteristicEvent.Notification, this.Value);
                    ob.OnNext(result);
                }
            })
                                                                                      )
                            .Publish()
                            .RefCount();

            return(this.notifyOb);
        }
예제 #3
0
        public override IObservable <ReadRequest> WhenReadReceived()
        {
            return(Observable.Create <ReadRequest>(ob =>
            {
                var handler = new EventHandler <CharacteristicReadEventArgs>((sender, args) =>
                {
                    if (!args.Characteristic.Equals(this.Native))
                    {
                        return;
                    }

                    var device = new Device(args.Device);
                    var request = new ReadRequest(device, args.Offset);
                    ob.OnNext(request);

                    lock (this.context.ServerReadWriteLock)
                    {
                        this.context.Server.SendResponse(
                            args.Device,
                            args.RequestId,
                            request.Status.ToNative(),
                            args.Offset,
                            request.Value
                            );
                    }
                });
                this.context.Callbacks.CharacteristicRead += handler;
                return () => this.context.Callbacks.CharacteristicRead -= handler;
            }));
        }
예제 #4
0
        public override IObservable <WriteRequest> WhenWriteReceived() => Observable.Create <WriteRequest>(ob =>
        {
            var handler = new EventHandler <CharacteristicWriteEventArgs>((sender, args) =>
            {
                if (!args.Characteristic.Equals(this.Native))
                {
                    return;
                }

                var device  = new Device(args.Device);
                var request = new WriteRequest(device, args.Value, args.Offset, args.ResponseNeeded);
                ob.OnNext(request);

                if (request.IsReplyNeeded)
                {
                    lock (this.context.ServerReadWriteLock)
                    {
                        this.context.Server.SendResponse
                        (
                            args.Device,
                            args.RequestId,
                            request.Status.ToNative(),
                            request.Offset,
                            request.Value
                        );
                    }
                }
            });
            this.context.Callbacks.CharacteristicWrite += handler;

            return(() => this.context.Callbacks.CharacteristicWrite -= handler);
        });
예제 #5
0
        public override IObservable <CharacteristicGattResult> Read()
        => this.context.Invoke(Observable.Create <CharacteristicGattResult>(ob =>
        {
            this.AssertRead();

            var sub = this.context
                      .Callbacks
                      .CharacteristicRead
                      .Where(this.NativeEquals)
                      .Subscribe(args =>
            {
                if (args.IsSuccessful)
                {
                    ob.Respond(new CharacteristicGattResult(this, args.Characteristic.GetValue()));
                }
                else
                {
                    ob.OnError(new BleException($"Failed to read characteristic - {args.Status}"));
                }
            });

            this.context.InvokeOnMainThread(() =>
            {
                if (!this.context.Gatt.ReadCharacteristic(this.native))
                {
                    ob.OnError(new BleException("Failed to read characteristic"));
                }
            });

            return(sub);
        }));
예제 #6
0
        public override IObservable <CharacteristicBroadcast> BroadcastObserve(byte[] value, params IDevice[] devices)
        {
            return(Observable.Create <CharacteristicBroadcast>(ob =>
            {
                var cancel = false;
                this.Native.SetValue(value);

                if (devices == null || devices.Length == 0)
                {
                    devices = this.subscribers.Values.ToArray();
                }

                var indicate = this.Properties.HasFlag(CharacteristicProperties.Indicate);
                foreach (var x in devices.OfType <Device>())
                {
                    if (!cancel)
                    {
                        lock (this.context.ServerReadWriteLock)
                        {
                            if (!cancel)
                            {
                                var result = this.context.Server.NotifyCharacteristicChanged(x.Native, this.Native, indicate);
                                ob.OnNext(new CharacteristicBroadcast(x, this, value, indicate, result));
                            }
                        }
                    }
                }

                ob.OnCompleted();
                return () => cancel = true;
            }));
        }
예제 #7
0
        public override IObservable <CharacteristicResult> WhenNotificationReceived()
        {
            this.AssertNotify();

            this.notifyOb = this.notifyOb ?? Observable.Create <CharacteristicResult>(ob =>
            {
                var handler = new EventHandler <GattCharacteristicEventArgs>((sender, args) =>
                {
                    if (this.NativeEquals(args))
                    {
                        if (!args.IsSuccessful)
                        {
                            ob.OnError(new ArgumentException("Error subscribing to " + args.Characteristic.Uuid));
                        }
                        else
                        {
                            this.Value = args.Characteristic.GetValue();

                            var result = new CharacteristicResult(this, CharacteristicEvent.Notification, this.Value);
                            ob.OnNext(result);
                        }
                    }
                });
                this.context.Callbacks.CharacteristicChanged += handler;

                return(() => this.context.Callbacks.CharacteristicChanged -= handler);
            })
                            .Publish()
                            .RefCount();

            return(this.notifyOb);
        }
예제 #8
0
        public override IObservable <CharacteristicResult> Read()
        {
            this.AssertRead();

            return(Observable.Create <CharacteristicResult>(ob =>
            {
                var handler = new EventHandler <GattCharacteristicEventArgs>((sender, args) =>
                {
                    if (args.Characteristic.Equals(this.native))
                    {
                        if (!args.IsSuccessful)
                        {
                            ob.OnError(new ArgumentException($"Failed to read characteristic - {args.Status}"));
                        }
                        else
                        {
                            this.Value = args.Characteristic.GetValue();

                            var result = new CharacteristicResult(this, CharacteristicEvent.Read, this.Value);
                            ob.Respond(result);
                            this.ReadSubject.OnNext(result);
                        }
                    }
                });
                this.context.Callbacks.CharacteristicRead += handler;
                this.context.Gatt.ReadCharacteristic(this.native);

                return () => this.context.Callbacks.CharacteristicRead -= handler;
            }));
        }
예제 #9
0
        public override IObservable <CharacteristicGattResult> WhenNotificationReceived()
        {
            this.AssertNotify();

            this.notifyOb = this.notifyOb ?? Observable.Create <CharacteristicGattResult>(ob =>
                                                                                          this.context
                                                                                          .Callbacks
                                                                                          .CharacteristicChanged
                                                                                          .Where(this.NativeEquals)
                                                                                          .Subscribe(args =>
            {
                if (args.IsSuccessful)
                {
                    ob.OnNext(new CharacteristicGattResult(this, args.Characteristic.GetValue()));
                }
                else
                {
                    ob.OnError(new BleException($"Notification error - {args.Status}"));
                }
            })
                                                                                          )
                            .Publish()
                            .RefCount();

            return(this.notifyOb);
        }
예제 #10
0
        public override IObservable <CharacteristicResult> Write(byte[] value)
        {
            this.AssertWrite(false);

            return(Observable.Create <CharacteristicResult>(async ob =>
            {
                CancellationTokenSource cancelSrc = null;

                var handler = new EventHandler <GattCharacteristicEventArgs>((sender, args) =>
                {
                    if (this.NativeEquals(args))
                    {
                        if (cancelSrc != null)
                        {
                            this.context.Semaphore.Release();
                        }

                        Log.Write("Incoming Characteristic Write Event - " + args.Characteristic.Uuid);

                        if (!args.IsSuccessful)
                        {
                            ob.OnError(new ArgumentException($"Failed to write characteristic - {args.Status}"));
                        }
                        else
                        {
                            this.Value = value;
                            var result = new CharacteristicResult(this, CharacteristicEvent.Write, this.Value);
                            ob.Respond(result);
                            this.WriteSubject.OnNext(result);
                        }
                    }
                });

                if (this.Properties.HasFlag(CharacteristicProperties.Write))
                {
                    cancelSrc = new CancellationTokenSource();

                    Log.Write("Hooking for write response - " + this.Uuid);

                    await this.context.Semaphore.WaitAsync(cancelSrc.Token);
                    this.context.Callbacks.CharacteristicWrite += handler;
                    this.RawWriteWithResponse(value);
                }
                else
                {
                    Log.Write("Write with No Response - " + this.Uuid);
                    this.RawWriteNoResponse(ob, value);
                }
                return () =>
                {
                    cancelSrc?.Dispose();
                    this.context.Callbacks.CharacteristicWrite -= handler;
                };
            }));
        }
예제 #11
0
        public IObservable <AppState> WhenStateChanged() => Observable.Create <AppState>(ob =>
        {
            var handler = new EventHandler((sender, args) =>
            {
                var state = this.appState.IsActive ? AppState.Foreground : AppState.Background;
                ob.OnNext(state);
            });
            this.appState.StatusChanged += handler;

            return(() => this.appState.StatusChanged -= handler);
        });
예제 #12
0
 public override IObservable <IGattDescriptor> DiscoverDescriptors()
 => this.descriptorOb ??= Observable.Create <IGattDescriptor>(ob =>
 {
     foreach (var nd in this.native.Descriptors)
     {
         var wrap = new GattDescriptor(this, this.context, nd);
         ob.OnNext(wrap);
     }
     return(Disposable.Empty);
 })
 .Replay()
 .RefCount();
예제 #13
0
        public override IObservable <DeviceSubscriptionEvent> WhenDeviceSubscriptionChanged()
        {
            this.subscriptionOb = this.subscriptionOb ?? Observable.Create <DeviceSubscriptionEvent>(ob =>
            {
                var handler = new EventHandler <DescriptorWriteEventArgs>((sender, args) =>
                {
                    if (args.Descriptor.Equals(this.NotificationDescriptor))
                    {
                        if (args.Value.SequenceEqual(NotifyEnabledBytes) || args.Value.SequenceEqual(IndicateEnableBytes))
                        {
                            var device = this.GetOrAdd(args.Device);
                            ob.OnNext(new DeviceSubscriptionEvent(device, true));
                        }
                        else
                        {
                            var device = this.Remove(args.Device);
                            if (device != null)
                            {
                                ob.OnNext(new DeviceSubscriptionEvent(device, false));
                            }
                        }
                    }
                });
                var dhandler = new EventHandler <ConnectionStateChangeEventArgs>((sender, args) =>
                {
                    if (args.NewState != ProfileState.Disconnected)
                    {
                        return;
                    }

                    var device = this.Remove(args.Device);
                    if (device != null)
                    {
                        ob.OnNext(new DeviceSubscriptionEvent(device, false));
                    }
                });

                this.context.Callbacks.ConnectionStateChanged += dhandler;
                this.context.Callbacks.DescriptorWrite        += handler;

                return(() =>
                {
                    this.context.Callbacks.DescriptorWrite -= handler;
                    this.context.Callbacks.ConnectionStateChanged -= dhandler;
                });
            })
                                  .Publish()
                                  .RefCount();

            return(this.subscriptionOb);
        }
예제 #14
0
파일: AppImpl.cs 프로젝트: ms666/deviceinfo
        public IObservable <Unit> WhenEnteringForeground() => Observable.Create <Unit>(ob =>
        {
            var handler = new EventHandler((sender, args) =>
            {
                if (this.appState.IsActive)
                {
                    Debug.WriteLine("Firing WhenEnteringForeground Observable");
                    ob.OnNext(Unit.Default);
                }
            });
            this.appState.StatusChanged += handler;

            return(() => this.appState.StatusChanged -= handler);
        });
예제 #15
0
        public override IObservable <CharacteristicResult> Read()
        {
            this.AssertRead();

            return(Observable.Create <CharacteristicResult>(async ob =>
            {
                var cancelSrc = new CancellationTokenSource();

                var handler = new EventHandler <GattCharacteristicEventArgs>((sender, args) =>
                {
                    if (this.NativeEquals(args))
                    {
                        this.context.Semaphore.Release();

                        if (!args.IsSuccessful)
                        {
                            ob.OnError(new ArgumentException($"Failed to read characteristic - {args.Status}"));
                        }
                        else
                        {
                            this.Value = args.Characteristic.GetValue();

                            var result = new CharacteristicResult(this, CharacteristicEvent.Read, this.Value);
                            ob.Respond(result);
                            this.ReadSubject.OnNext(result);
                        }
                    }
                });
                this.context.Callbacks.CharacteristicRead += handler;
                await this.context.Semaphore.WaitAsync(cancelSrc.Token);

                this.context.Marshall(() =>
                {
                    try
                    {
                        this.context.Gatt.ReadCharacteristic(this.native);
                    }
                    catch (Exception ex)
                    {
                        ob.OnError(ex);
                    }
                });

                return () => this.context.Callbacks.CharacteristicRead -= handler;
            }));
        }
예제 #16
0
 public override IObservable <IGattCharacteristic?> GetKnownCharacteristic(string characteristicUuid, bool throwIfNotFound = false)
 => Observable.Create <IGattCharacteristic?>(ob =>
 {
     var uuid = UUID.FromString(characteristicUuid);
     var cs   = this.native.GetCharacteristic(uuid);
     if (cs == null)
     {
         ob.Respond(null);
     }
     else
     {
         var characteristic = new GattCharacteristic(this, this.context, cs);
         ob.Respond(characteristic);
     }
     return(Disposable.Empty);
 })
 .Assert(this.Uuid, characteristicUuid, throwIfNotFound);
예제 #17
0
        public override IObservable <HttpTransfer> WhenUpdated()
        {
            // TODO: cancel/error, should remove from db
            var query = new QueryFilter().ToNative();

            this.httpObs = this.httpObs ?? Observable
                           .Create <HttpTransfer>(ob =>
            {
                var lastRun  = DateTime.UtcNow;
                var disposer = new CompositeDisposable();

                HttpTransferBroadcastReceiver
                .HttpEvents
                .Subscribe(ob.OnNext)
                .DisposedBy(disposer);

                Observable
                .Interval(TimeSpan.FromSeconds(2))
                .Subscribe(_ =>
                {
                    using (var cursor = this.context.GetManager().InvokeQuery(query))
                    {
                        while (cursor.MoveToNext())
                        {
                            var lastModEpoch = cursor.GetLong(cursor.GetColumnIndex(Native.ColumnLastModifiedTimestamp));
                            var epoch        = DateTimeOffset.FromUnixTimeMilliseconds(lastModEpoch);
                            if (epoch > lastRun)
                            {
                                var transfer = cursor.ToLib();
                                ob.OnNext(transfer);
                            }
                        }
                    }

                    lastRun = DateTime.UtcNow;
                })
                .DisposedBy(disposer);

                return(disposer);
            })
                           .Publish()
                           .RefCount();

            return(this.httpObs.Merge(base.WhenUpdated()));
        }
예제 #18
0
        public IObservable <object> WhenEnteringBackground()
        {
            return(Observable.Create <object>(ob =>
            {
                var handler = new EventHandler((sender, args) =>
                {
                    Debug.WriteLine("Firing 1 WhenEnteringBackground Observable");

                    if (!this.appState.IsActive)
                    {
                        Debug.WriteLine("Firing WhenEnteringBackground Observable");
                        ob.OnNext(null);
                    }
                });
                this.appState.StatusChanged += handler;

                return () => this.appState.StatusChanged -= handler;
            }));
        }
예제 #19
0
        public IObservable <ScanResult> Scan(ScanConfig config) => Observable.Create <ScanResult>(ob =>
        {
            this.devices.Clear();

            this.callbacks = new LollipopScanCallback(
                sr =>
            {
                var scanResult = this.ToScanResult(sr.Device, sr.Rssi, new AdvertisementData(sr));
                ob.OnNext(scanResult);
            },
                errorCode => ob.OnError(new BleException("Error during scan: " + errorCode.ToString()))
                );

            var builder  = new ScanSettings.Builder();
            var scanMode = this.ToNative(config.ScanType);
            builder.SetScanMode(scanMode);

            var scanFilters = new List <ScanFilter>();
            if (config.ServiceUuids != null && config.ServiceUuids.Count > 0)
            {
                foreach (var uuid in config.ServiceUuids)
                {
                    var parcel = new ParcelUuid(UUID.FromString(uuid));
                    scanFilters.Add(new ScanFilter.Builder()
                                    .SetServiceUuid(parcel)
                                    .Build()
                                    );
                }
            }

            if (config.AndroidUseScanBatching && this.Manager.Adapter.IsOffloadedScanBatchingSupported)
            {
                builder.SetReportDelay(100);
            }

            this.Manager.Adapter.BluetoothLeScanner.StartScan(
                scanFilters,
                builder.Build(),
                this.callbacks
                );

            return(() => this.Manager.Adapter.BluetoothLeScanner?.StopScan(this.callbacks));
        });
예제 #20
0
        public override IObservable <object> Write(byte[] value)
        {
            this.AssertWrite(false);

            return(Observable.Create <object>(ob =>
            {
                var handler = new EventHandler <GattCharacteristicEventArgs>((sender, args) =>
                {
                    if (args.Characteristic.Uuid.Equals(this.native.Uuid))
                    {
                        if (!args.IsSuccessful)
                        {
                            ob.OnError(new ArgumentException($"Failed to write characteristic - {args.Status}"));
                        }
                        else
                        {
                            this.Value = value;
                            ob.Respond(this.Value);
                            this.WriteSubject.OnNext(this.Value);
                        }
                    }
                });
                this.context.Callbacks.CharacteristicWrite += handler;
                this.native.SetValue(value);

                if (this.Properties.HasFlag(CharacteristicProperties.Write))
                {
                    this.native.WriteType = GattWriteType.Default;
                    this.context.Gatt.WriteCharacteristic(this.native);
                }
                else
                {
                    this.native.WriteType = GattWriteType.NoResponse;
                    this.context.Gatt.WriteCharacteristic(this.native);
                    this.Value = value;
                    ob.Respond(this.Value);
                    this.WriteSubject.OnNext(this.Value);
                }
                return () => this.context.Callbacks.CharacteristicWrite -= handler;
            }));
        }
예제 #21
0
        public override IObservable <CharacteristicResult> Write(byte[] value)
        {
            this.AssertWrite(false);

            return(Observable.Create <CharacteristicResult>(ob =>
            {
                var handler = new EventHandler <GattCharacteristicEventArgs>((sender, args) =>
                {
                    WriteLine($"Incoming Characteristic Write Event - " + args.Characteristic.Uuid);

                    if (args.Characteristic.Equals(this.native))
                    {
                        if (!args.IsSuccessful)
                        {
                            ob.OnError(new ArgumentException($"Failed to write characteristic - {args.Status}"));
                        }
                        else
                        {
                            this.Value = value;
                            var result = new CharacteristicResult(this, CharacteristicEvent.Write, this.Value);
                            ob.Respond(result);
                            this.WriteSubject.OnNext(result);
                        }
                    }
                });

                if (this.Properties.HasFlag(CharacteristicProperties.Write))
                {
                    WriteLine("Hooking for write response - " + this.Uuid);
                    this.context.Callbacks.CharacteristicWrite += handler;
                    this.RawWriteWithResponse(value);
                }
                else
                {
                    WriteLine("Write with No Response - " + this.Uuid);
                    this.RawWriteNoResponse(ob, value);
                }
                return () => this.context.Callbacks.CharacteristicWrite -= handler;
            }));
        }
예제 #22
0
        public override IObservable <CharacteristicGattResult> Write(byte[] value)
        => this.context.Invoke(Observable.Create <CharacteristicGattResult>(ob =>
        {
            this.AssertWrite(false);

            var sub = this.context
                      .Callbacks
                      .CharacteristicWrite
                      .Where(this.NativeEquals)
                      .Subscribe(args =>
            {
                Log.Debug(BleLogCategory.Characteristic, "write event - " + args.Characteristic.Uuid);
                if (args.IsSuccessful)
                {
                    ob.Respond(new CharacteristicGattResult(this, value));
                }
                else
                {
                    ob.OnError(new BleException($"Failed to write characteristic - {args.Status}"));
                }
            });

            Log.Debug(BleLogCategory.Characteristic, "Hooking for write response - " + this.Uuid);
            this.context.InvokeOnMainThread(() =>
            {
                this.native.WriteType = GattWriteType.Default;
                this.native.SetValue(value);
                //if (!this.native.SetValue(value))
                //ob.OnError(new BleException("Failed to set characteristic value"));

                //else if (!this.context.Gatt.WriteCharacteristic(this.native))
                if (!this.context.Gatt.WriteCharacteristic(this.native))
                {
                    ob.OnError(new BleException("Failed to write to characteristic"));
                }
            });

            return(sub);
        }));
예제 #23
0
        public override IObservable <byte[]> SubscribeToNotifications()
        {
            this.AssertNotify();

            this.notifyOb = this.notifyOb ?? Observable.Create <byte[]>(ob =>
            {
                var handler = new EventHandler <GattCharacteristicEventArgs>((sender, args) =>
                {
                    if (args.Characteristic.Uuid.Equals(this.native.Uuid))
                    {
                        if (!args.IsSuccessful)
                        {
                            ob.OnError(new ArgumentException("Error subscribing to " + args.Characteristic.Uuid));
                        }
                        else
                        {
                            this.Value = args.Characteristic.GetValue();
                            ob.OnNext(this.Value);
                            this.NotifySubject.OnNext(this.Value);
                        }
                    }
                });
                this.EnableNotifications();
                this.context.Callbacks.CharacteristicChanged += handler;

                return(() =>
                {
                    this.DisableNotifications();
                    this.context.Callbacks.CharacteristicChanged -= handler;
                });
            })
                            .Publish()
                            .RefCount();

            return(this.notifyOb);
        }
예제 #24
0
        public override IObservable <CharacteristicGattResult> Write(byte[] value, bool withResponse) => this.context.Invoke(Observable.Create <CharacteristicGattResult>(ob =>
        {
            this.AssertWrite(false);

            var sub = this.context
                      .Callbacks
                      .CharacteristicWrite
                      .Where(this.NativeEquals)
                      .Subscribe(args =>
            {
                Log.Write("BLE-Characteristic", "write event - " + args.Characteristic.Uuid);

                if (args.IsSuccessful)
                {
                    ob.Respond(new CharacteristicGattResult(this, value, withResponse ? CharacteristicResultType.Write : CharacteristicResultType.WriteWithoutResponse));
                }
                else
                {
                    ob.OnError(new BleException($"Failed to write characteristic - {args.Status}"));
                }
            });

            Log.Write("BLE-Characteristic", "Hooking for write response - " + this.Uuid);
            this.context.InvokeOnMainThread(() =>
            {
                try
                {
                    // TODO: signed write
                    this.native.WriteType = withResponse ? GattWriteType.Default : GattWriteType.NoResponse;
                    this.native.SetValue(value);
                    //if (!this.native.SetValue(value))
                    //ob.OnError(new BleException("Failed to set characteristic value"));

                    //else if (!this.context.Gatt.WriteCharacteristic(this.native))
                    if (!this.context.Gatt?.WriteCharacteristic(this.native) ?? false)
                    {
                        ob.OnError(new BleException("Failed to write to characteristic"));
                    }
                }
                catch (Exception ex)
                {
                    ob.OnError(ex);
                }
            });

            return(sub);
        }));
예제 #25
0
        internal static IObservable <TResult> SelectMany <TSource, TContext, TCollection, TResult>(
            this IObservable <TSource> source,
            TContext context,
            Func <TContext, Tuple <TSource, bool>, Tuple <TContext, IObservable <TCollection> > > firstCollectionSelector,
            IEnumerable <Func <TContext, Tuple <TCollection, bool>, Tuple <TContext, IObservable <TCollection> > > > otherCollectionSelectors,
            Func <TSource, IList <TCollection>, TResult> resultSelector)
        {
            Contract.Requires(source != null);
            Contract.Requires(context != null);
            Contract.Requires(firstCollectionSelector != null);
            Contract.Requires(otherCollectionSelectors != null);
            Contract.Requires(resultSelector != null);
            Contract.Ensures(Contract.Result <IObservable <TResult> >() != null);

            return(Observable.Create <TResult>(
                       observer =>
            {
                object gate = new object();

                var disposables = new CompositeDisposable();

                int running = 0;
                bool sourceCompleted = false;

                Action <Action> checkCompleted =
                    change =>
                {
                    bool completeNow = false;

                    lock (gate)
                    {
                        change();

                        completeNow = running == 0 && sourceCompleted;
                    }

                    if (completeNow)
                    {
                        observer.OnCompleted();
                    }
                };

                Action <Func <IObservable <TCollection> >, Action <Tuple <TCollection, bool> > > subscribe =
                    (selector, onNext) =>
                {
                    IObservable <TCollection> sequence;

                    try
                    {
                        sequence = selector();
                    }
                    catch (Exception ex)
                    {
                        observer.OnError(ex);
                        return;
                    }

                    var subscription = new SingleAssignmentDisposable();

                    disposables.Add(subscription);

                    Interlocked.Increment(ref running);

                    subscription.Disposable = sequence.WithLastElementIndicator().SubscribeSafe(
                        onNext,
                        observer.OnError,
                        () =>
                    {
                        checkCompleted(() => Interlocked.Decrement(ref running));

                        disposables.Remove(subscription);
                    });
                };

                object selectorCacheGate = new object();

                var selectorCache = new List <Func <TContext, Tuple <TCollection, bool>, Tuple <TContext, IObservable <TCollection> > > >();

                var enumerator = otherCollectionSelectors.GetEnumerator();

                disposables.Add(Disposable.Create(() =>
                {
                    lock (selectorCacheGate)
                    {
                        enumerator.Dispose();
                        enumerator = null;
                    }
                }));

                Func <int, Func <TContext, Tuple <TCollection, bool>, Tuple <TContext, IObservable <TCollection> > > > getSelector =
                    depth =>
                {
                    Func <TContext, Tuple <TCollection, bool>, Tuple <TContext, IObservable <TCollection> > > selector = null;

                    lock (selectorCacheGate)
                    {
                        if (enumerator == null)
                        {
                            return null;
                        }
                        else if (selectorCache.Count > depth)
                        {
                            selector = selectorCache[depth];
                        }
                        else if (enumerator.MoveNext())
                        {
                            selector = enumerator.Current;

                            selectorCache.Add(selector);
                        }
                        else
                        {
                            selectorCache.Add(null);
                        }
                    }

                    return selector;
                };

                Func <int, TContext, TSource, IEnumerable <TCollection>, Action <Tuple <TCollection, bool> > > onNextSequential = null;
                onNextSequential = (depth, currentContext, first, others) =>
                                   value =>
                {
                    var list = others.ToList();

                    list.Add(value.Item1);

                    var selector = getSelector(depth);

                    if (selector != null)
                    {
                        var sequence = selector(currentContext, value);

                        subscribe(
                            () => sequence.Item2,
                            onNextSequential(depth + 1, sequence.Item1, first, list));
                    }
                    else
                    {
                        TResult result;

                        try
                        {
                            result = resultSelector(first, list.AsReadOnly());
                        }
                        catch (Exception ex)
                        {
                            observer.OnError(ex);
                            return;
                        }

                        observer.OnNext(result);
                    }
                };

                disposables.Add(source.WithLastElementIndicator().SubscribeSafe(
                                    first =>
                {
                    var sequence = firstCollectionSelector(context, first);

                    subscribe(
                        () => sequence.Item2,
                        onNextSequential(0, sequence.Item1, first.Item1, Enumerable.Empty <TCollection>()));
                },
                                    observer.OnError,
                                    () => checkCompleted(() => sourceCompleted = true)));

                return disposables;
            }));
        }
예제 #26
0
        public override IObservable <IGattCharacteristic> EnableNotifications(bool enable, bool useIndicationsIfAvailable) => this.context.Invoke(Observable.Create <IGattCharacteristic>(ob =>
        {
            if (!this.context.Gatt.SetCharacteristicNotification(this.native, enable))
            {
                throw new BleException("Failed to set characteristic notification value");
            }

            IDisposable?sub = null;
            var descriptor  = this.native.GetDescriptor(NotifyDescriptorId);
            if (descriptor == null)
            {
                throw new ArgumentException("Notification descriptor not found");
            }

            var wrap  = new GattDescriptor(this, this.context, descriptor);
            var bytes = enable
                ? this.GetNotifyDescriptorBytes(useIndicationsIfAvailable)
                : BluetoothGattDescriptor.DisableNotificationValue.ToArray();

            sub = wrap
                  .WriteInternal(bytes)
                  .Subscribe(
                _ =>
            {
                this.IsNotifying = enable;
                ob.Respond(this);
            },
                ob.OnError
                );
            return(() => sub?.Dispose());
        }));
예제 #27
0
        public override IObservable <GattCharacteristicResult> Write(byte[] value, bool withResponse = true) => this.context.Invoke(Observable.Create <GattCharacteristicResult>((Func <IObserver <GattCharacteristicResult>, IDisposable>)(ob =>
        {
            this.AssertWrite(withResponse);

            var sub = this.context
                      .Callbacks
                      .CharacteristicWrite
                      .Where(this.NativeEquals)
                      .Subscribe(args =>
            {
                if (!args.IsSuccessful)
                {
                    ob.OnError(new BleException($"Failed to write characteristic - {args.Status}"));
                }
                else
                {
                    var writeType = withResponse
                            ? GattCharacteristicResultType.Write
                            : GattCharacteristicResultType.WriteWithoutResponse;

                    ob.Respond(new GattCharacteristicResult(this, value, writeType));
                }
            });

            this.context.InvokeOnMainThread(() =>
            {
                try
                {
                    this.native.WriteType = withResponse ? GattWriteType.Default : GattWriteType.NoResponse;
                    var authSignedWrite   =
                        this.native.Properties.HasFlag(GattProperty.SignedWrite) &&
                        this.context.NativeDevice.BondState == Bond.Bonded;

                    if (authSignedWrite)
                    {
                        this.native.WriteType |= GattWriteType.Signed;
                    }

                    this.native.SetValue(value);
                    //if (!this.native.SetValue(value))
                    //ob.OnError(new BleException("Failed to set characteristic value"));

                    //else if (!this.context.Gatt.WriteCharacteristic(this.native))
                    if (!this.context.Gatt?.WriteCharacteristic(this.native) ?? false)
                    {
                        ob.OnError(new BleException("Failed to write to characteristic"));
                    }
                }
                catch (Exception ex)
                {
                    ob.OnError(ex);
                }
            });

            return(sub);
        })));
예제 #28
0
        public override IObservable <GattCharacteristicResult> Read() => this.context.Invoke(Observable.Create <GattCharacteristicResult>((Func <IObserver <GattCharacteristicResult>, IDisposable>)(ob =>
        {
            this.AssertRead();

            var sub = this.context
                      .Callbacks
                      .CharacteristicRead
                      .Where(this.NativeEquals)
                      .Subscribe(args =>
            {
                if (!args.IsSuccessful)
                {
                    ob.OnError(new BleException($"Failed to read characteristic - {args.Status}"));
                }
                else
                {
                    var value  = args.Characteristic.GetValue();
                    var result = new GattCharacteristicResult(this, value, GattCharacteristicResultType.Read);
                    ob.Respond(result);
                }
            });

            this.context.InvokeOnMainThread(() =>
            {
                try
                {
                    if (!this.context.Gatt?.ReadCharacteristic(this.native) ?? false)
                    {
                        throw new BleException("Failed to read characteristic");
                    }
                }
                catch (Exception ex)
                {
                    ob.OnError(ex);
                }
            });

            return(sub);
        })));
예제 #29
0
        public override IObservable <CharacteristicResult> Write(byte[] value) => this.context.Lock(Observable.Create <CharacteristicResult>(async ob =>
        {
            this.AssertWrite(false);

            Log.Debug("Characteristic", "past write gate");
            var sub = this.context
                      .Callbacks
                      .CharacteristicWrite
                      .Where(this.NativeEquals)
                      .Subscribe(args =>
            {
                Log.Debug("Characteristic", "write vent - " + args.Characteristic.Uuid);

                if (!args.IsSuccessful)
                {
                    ob.OnError(new ArgumentException($"Failed to write characteristic - {args.Status}"));
                }
                else
                {
                    this.Value = value;
                    var result = new CharacteristicResult(this, CharacteristicEvent.Write, this.Value);
                    ob.Respond(result);
                    this.WriteSubject.OnNext(result);
                }
            });

            if (this.Properties.HasFlag(CharacteristicProperties.Write))
            {
                Log.Debug("Characteristic", "Hooking for write response - " + this.Uuid);
                await this.RawWriteWithResponse(value);
            }
            else
            {
                Log.Debug("Characteristic", "Write with No Response - " + this.Uuid);
                await this.RawWriteNoResponse(ob, value);
            }
            return(sub);
        }));
예제 #30
0
        public override IObservable <CharacteristicResult> Read() => this.context.Lock(Observable.Create <CharacteristicResult>(async ob =>
        {
            this.AssertRead();

            var sub = this.context
                      .Callbacks
                      .CharacteristicRead
                      .Where(this.NativeEquals)
                      .Subscribe(args =>
            {
                if (!args.IsSuccessful)
                {
                    ob.OnError(new ArgumentException($"Failed to read characteristic - {args.Status}"));
                }
                else
                {
                    this.Value = args.Characteristic.GetValue();

                    var result = new CharacteristicResult(this, CharacteristicEvent.Read, this.Value);
                    ob.Respond(result);
                    this.ReadSubject.OnNext(result);
                }
            });

            await this.context.Marshall(() =>
            {
                try
                {
                    if (!this.context.Gatt.ReadCharacteristic(this.native))
                    {
                        ob.OnError(new Exception($"Failed to read characteristic."));
                    }
                }
                catch (Exception ex)
                {
                    ob.OnError(ex);
                }
            });

            return(sub);
        }));