protected AppletChannel(Guid appletId, IAppInfo appInfo) { _appInfo = appInfo ?? throw new ArgumentNullException(nameof(appInfo)); if (false == appInfo.IsAppletId(appletId)) { throw new ArgumentException($"Invalid applet ID. Application: {appInfo.ApplicationName}, Applet ID: {appletId}"); } AppletId = appletId; Instance = Guid.NewGuid(); var disposing = _cancellationSource.Token.ToObservable(); _privateResponses = Observable .Defer(CreatePrivateResponsesObservable) .TakeUntil(disposing) .ObserveOn(_privateEventLoopScheduler) .Do(InspectPrivateResponse) .Do(args => Pulse()) .Where(IsValidResponse) .Publish(); _privateResponses .SubscribeOn(_privateEventLoopScheduler) .Where(response => _privateResponseObserversByCorrelationId.ContainsKey(response.CorrelationId)) .Subscribe( OnPrivateResponse, OnPrivateResponsesError, OnPrivateResponsesCompleted); Observable .Interval(_appInfo.HeartbeatInterval) .TakeUntil(disposing) .SkipUntil(_pulse) .Select(CreateHeartbeatDto) .Select(ToDispatchArgs) .Subscribe(args => { args.IntentId = AppInfo.HeartbeatIntent; SendAsync(args); }); }