public void TestInitialFalse()
        {
            var notifier = new BooleanNotifier(false);
            var recorder = new TestScheduler().CreateObserver<bool>();
            notifier.Subscribe(recorder);

            notifier.Value.Is(false);

            notifier.TurnOn();
            notifier.TurnOff();
            notifier.Value = true;
            notifier.Value = true;
            notifier.Value = false;
            notifier.TurnOn();
            notifier.SwitchValue();
            notifier.SwitchValue();

            recorder.Messages.Is(
                OnNext(0, true),
                OnNext(0, false),
                OnNext(0, true),
                OnNext(0, true),
                OnNext(0, false),
                OnNext(0, true),
                OnNext(0, false),
                OnNext(0, true));

            recorder.Messages.Clear();

            notifier.TurnOn();
            recorder.Messages.Count.Is(0);
        }
    public void TestInitialFalse()
    {
        var notifier = new BooleanNotifier(false);
        var recorder = new TestScheduler().CreateObserver <bool>();

        notifier.Subscribe(recorder);

        notifier.Value.Is(false);

        notifier.TurnOn();
        notifier.TurnOff();
        notifier.Value = true;
        notifier.Value = true;
        notifier.Value = false;
        notifier.TurnOn();
        notifier.SwitchValue();
        notifier.SwitchValue();

        recorder.Messages.Is(
            OnNext(0, true),
            OnNext(0, false),
            OnNext(0, true),
            OnNext(0, true),
            OnNext(0, false),
            OnNext(0, true),
            OnNext(0, false),
            OnNext(0, true));

        recorder.Messages.Clear();

        notifier.TurnOn();
        recorder.Messages.Count.Is(0);
    }
        public TimerStartCommandViewModel()
        {
            // ctorからカウント開始
            var ctorDateTime = DateTime.Now;

            MyTimer1 = Observable.Interval(TimeSpan.FromSeconds(1))
                       .Select(_ => DateTime.Now - ctorDateTime)
                       .ToReadOnlyReactivePropertySlim()
                       .AddTo(CompositeDisposable);

#if false
            // 1. DataTime.Now からの差分でカウント  ◆もう少しかっちょよい実装ない?
            var clickDateTime = DateTime.MinValue;
            MyTimer2 = Observable.Interval(TimeSpan.FromSeconds(1))
                       .Where(_ => clickDateTime != DateTime.MinValue)
                       .Select(_ => DateTime.Now - clickDateTime)
                       .ToReadOnlyReactivePropertySlim()
                       .AddTo(CompositeDisposable);

            StartCommand = new ReactiveCommand()
                           .WithSubscribe(() => clickDateTime = DateTime.Now, CompositeDisposable.Add);

            StopCommand = new ReactiveCommand()
                          .WithSubscribe(() => clickDateTime = DateTime.MinValue, CompositeDisposable.Add);
#else
            // 2. BooleanNotifier で管理。Command.CanExecute も取れるので良い感じ
            var timerRunning = new BooleanNotifier();       // BooleanNotifier は IDisposable じゃないので Good!
            //var timerRunning = new ReactivePropertySlim<bool>(initialValue: false).AddTo(CompositeDisposable);

            MyTimer2 = Observable.Interval(TimeSpan.FromSeconds(1))
                       .TakeWhile(_ => timerRunning.Value)
                       .Repeat()
                       .Select(sec => TimeSpan.FromSeconds(sec))
                       .ToReadOnlyReactivePropertySlim()
                       .AddTo(CompositeDisposable);

            StartCommand = timerRunning.Inverse()
                           .ToReactiveCommand(!timerRunning.Value)
                           .WithSubscribe(() => timerRunning.TurnOn(), CompositeDisposable.Add);

            // ◆BooleanNotifier は (Rpと違って) Subscribe 時に LatestValue を発行しないので初期値の指定が必要する
            StopCommand = timerRunning
                          .ToReactiveCommand(timerRunning.Value)
                          .WithSubscribe(() => timerRunning.TurnOff(), CompositeDisposable.Add);
#endif
        }
        internal async Task <bool> SetInputtedKeyAsync(Key actualKey, BooleanNotifier notMatchedKeyNotifier, BooleanNotifier keyMissingNotifier)
        {
            if (actualKey.TryToChar(out var actualChar))
            {
                this.KeyInputtedCount.Value++;
                this.InputtedKey.Value = actualChar;
                if (actualKey == this.ExpectedKey.Value.ToKey())
                {
                    notMatchedKeyNotifier.TurnOff();
                    return(true);
                }

                this.KeyMistakedCount.Value++;
                notMatchedKeyNotifier.TurnOn();
                keyMissingNotifier.TurnOn();
                var task1 = AppContextService.BeepAsync();
                var task2 = Task.Delay(TimeSpan.FromSeconds(0.2));
                await Task.WhenAny(task1, task2);

                keyMissingNotifier.TurnOff();
            }

            return(false);
        }
        public DevicePageViewModel(UnconnectedImbleDevice unconnectedDevice)
        {
            this.Device = Observable.FromAsync(token => unconnectedDevice.Connect(token))
                          .CatchIgnore((Exception e) => { })
                          .ToReadOnlyReactiveProperty()
                          .AddTo(this.disposables);

            this.IsConnected = this.Device.Select(device => device != null).ToReadOnlyReactiveProperty().AddTo(this.disposables);

            this.DeviceStatus = this.Device
                                .Where(device => device != null)
                                .Select(device => device.ObserveProperty(self => self.Status))
                                .Switch()
                                .Do(value => Debug.WriteLine(value))
                                .ObserveOnUIDispatcher()
                                .ToReadOnlyReactiveProperty()
                                .AddTo(this.disposables);

            this.ReceivedMessage = this.Device
                                   .Where(device => device != null)
                                   .Select(device => Observable.FromEventPattern <DataArrivedEventArgs>(handler => device.DataArrived += handler, handler => device.DataArrived -= handler))
                                   .Switch()
                                   .Select(args => new ReceivedMessageViewModel(args.EventArgs.Data, args.EventArgs.Timestamp))
                                   .OnErrorRetry()
                                   .ToReadOnlyReactiveProperty()
                                   .AddTo(this.disposables);

            var notBusyNotifier = new BooleanNotifier(false);

            this.Name = new ReactiveProperty <string>("Fuga")
                        .SetValidateNotifyError(value => value != null && Encoding.UTF8.GetByteCount(value) < 12 ? null : "Input short message")
                        .AddTo(this.disposables);

            this.CanSendCommand = Observable.CombineLatest(
                this.DeviceStatus.Select(status => status == ImbleDeviceStatus.Running),
                this.Name.ObserveHasErrors,
                notBusyNotifier.Do(value => Debug.WriteLine(value)),
                (isRunning, hasErrors, notBusy) => isRunning && !hasErrors && notBusy)
                                  .ToReadOnlyReactiveProperty().AddTo(this.disposables);
            notBusyNotifier.TurnOn();

            this.SendCommand = this.CanSendCommand.ToReactiveCommand().AddTo(this.disposables);
            this.SendCommand
            .Do(_ => notBusyNotifier.TurnOff())
            .Select(_ =>
            {
                var data = Encoding.UTF8.GetBytes(this.Name.Value);
                return(Observable.FromAsync(token =>
                {
                    using (var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
                        using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, timeoutTokenSource.Token))
                        {
                            return this.Device.Value.SendAsync(data, 0, data.Length, linkedTokenSource.Token);
                        }
                })
                       .Finally(() => notBusyNotifier.TurnOn()));
            })
            .Switch()
            .OnErrorRetry((Exception e) => Debug.WriteLine(e))
            .Subscribe()
            .AddTo(this.disposables);
        }
        public DevicePageViewModel(UnconnectedImbleDevice unconnectedDevice)
        {
            this.Device = Observable.FromAsync(token => unconnectedDevice.Connect(token))
                .CatchIgnore((Exception e) => { })
                .ToReadOnlyReactiveProperty()
                .AddTo(this.disposables);

            this.IsConnected = this.Device.Select(device => device != null).ToReadOnlyReactiveProperty().AddTo(this.disposables);

            this.DeviceStatus = this.Device
                .Where(device => device != null)
                .Select(device => device.ObserveProperty(self => self.Status))
                .Switch()
                .Do(value => Debug.WriteLine(value))
                .ObserveOnUIDispatcher()
                .ToReadOnlyReactiveProperty()
                .AddTo(this.disposables);

            this.ReceivedMessage = this.Device
                .Where(device => device != null)
                .Select(device => Observable.FromEventPattern<DataArrivedEventArgs>(handler => device.DataArrived += handler, handler => device.DataArrived -= handler))
                .Switch()
                .Select(args => new ReceivedMessageViewModel(args.EventArgs.Data, args.EventArgs.Timestamp))
                .OnErrorRetry()
                .ToReadOnlyReactiveProperty()
                .AddTo(this.disposables);

            var notBusyNotifier = new BooleanNotifier(false);
            
            this.Name = new ReactiveProperty<string>("Fuga")
                .SetValidateNotifyError(value => value != null && Encoding.UTF8.GetByteCount(value) < 12 ? null : "Input short message")
                .AddTo(this.disposables);

            this.CanSendCommand = Observable.CombineLatest(
                this.DeviceStatus.Select(status => status == ImbleDeviceStatus.Running),
                this.Name.ObserveHasErrors,
                notBusyNotifier.Do(value => Debug.WriteLine(value)),
                (isRunning, hasErrors, notBusy) => isRunning && !hasErrors && notBusy)
                .ToReadOnlyReactiveProperty().AddTo(this.disposables);
            notBusyNotifier.TurnOn();

            this.SendCommand = this.CanSendCommand.ToReactiveCommand().AddTo(this.disposables);
            this.SendCommand
                .Do(_ => notBusyNotifier.TurnOff())
                .Select(_ =>
                {
                    var data = Encoding.UTF8.GetBytes(this.Name.Value);
                        return Observable.FromAsync(token =>
                        {
                            using (var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
                            using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, timeoutTokenSource.Token))
                            {
                                return this.Device.Value.SendAsync(data, 0, data.Length, linkedTokenSource.Token);
                            }
                        })
                        .Finally(() => notBusyNotifier.TurnOn());
                })
                .Switch()
                .OnErrorRetry((Exception e) => Debug.WriteLine(e))
                .Subscribe()
                .AddTo(this.disposables);
        }