private async Task RepeatGetStateAsync(int repeatMillis, CancellationTokenSource tokenSource)
        {
            while (true)
            {
                if (IsCommandInProgress)
                {
                    continue;
                }

                try
                {
                    // TODO(): Handle timeout and other exceptions here.
                    StateResponse stateCheckResult =
                        await m_coreLink.Request(new GetStateConversation(), DefaultKeepaliveTimeoutMs)
                        .ConfigureAwait(false);

                    // Check this again - the cancellation could have come during the request.
                    if (!tokenSource.IsCancellationRequested)
                    {
                        m_stateResultAction(new KeepaliveResult(stateCheckResult));
                    }
                }
                catch (Exception ex)
                {
                    // TODO(HonzaS): if this keeps on failing, notify the user.
                    Log.Warn("Periodic state check failed: {message}", ex.Message);
                    m_stateResultAction(new KeepaliveResult(KeepaliveResultTag.RequestFailed));
                }

                try
                {
                    await Task.Delay(repeatMillis, tokenSource.Token).ConfigureAwait(false);
                }
                catch (Exception ex)
                {
                    if (ex is TaskCanceledException)
                    {
                        return;
                    }

                    Log.Warn(ex, "Task.Delay threw an exception");
                }
            }
        }
        public async Task GetsAsyncConversationError()
        {
            const string errorMessage = "Foo bar";

            var       conv            = new CommandConversation(CommandType.Run);
            var       responseMessage = ErrorResponseBuilder.Build(errorMessage);
            ICoreLink coreLink        = GenerateCoreLink(responseMessage);

            var ex = await Assert.ThrowsAsync <RemoteCoreException>(() => coreLink.Request(conv, WaitMs));

            Assert.Equal(errorMessage, ex.Message);
        }
        public void GetsAsyncConversationResult()
        {
            var conversation = new CommandConversation(CommandType.Run);

            ResponseMessage responseMessage = StateResponseBuilder.Build(StateType.Running);

            ICoreLink coreLink = GenerateCoreLink(responseMessage);

            Task <StateResponse> futureResponse = coreLink.Request(conversation, WaitMs);

            StateResponse receivedResponse = ReadResponse(futureResponse);

            Assert.Equal(StateType.Running, receivedResponse.State);
        }
        // TODO(HonzaS): Add filtering.
        private async Task RepeatGetModelAsync(CancellationTokenSource cancellation)
        {
            // TODO(HonzaS): If a command is in progress and visualization is fast enough, this actively waits (loops).
            // Can we replace this with another reset event?
            ModelResponse modelResponse = null;

            while (true)
            {
                if (await WaitForEvent(m_requestModelEvent, cancellation) == WaitEventResult.Cancelled)
                {
                    return;
                }

                if (m_coreController.IsCommandInProgress)
                {
                    continue;
                }

                try
                {
                    // If there is no change to the filter, send null.
                    ModelFilter filterToSend = m_filterChanged ? m_filter : null;
                    m_filterChanged = false;

                    // Request a model diff from the core.
                    // TODO(HonzaS): Unless we lost connection or there was an error, request only incremental model (full: false).
                    var modelResponseTask = m_coreLink.Request(new GetModelConversation(m_getFullModel, filterToSend, m_observerRequests), TimeoutMs).ConfigureAwait(false);
                    m_getFullModel = false;

                    // Wait until the model has been read. This happens before the first request as well.
                    if (await WaitForEvent(m_modelReadEvent, cancellation) == WaitEventResult.Cancelled)
                    {
                        return;
                    }

                    // Wait for the previous diff to be applied to the new model (skip if this is the first request).
                    if (modelResponse != null)
                    {
                        await ApplyModelDiffAsync(modelResponse);
                    }

                    // Wait for a new diff from the core.
                    modelResponse = await modelResponseTask;

                    // Apply current diff to the new model.
                    await ApplyModelDiffAsync(modelResponse);

                    // Allow visualization to read current (updated) model.
                    m_isNewModelReady = true;
                }
                catch (Exception exception)
                {
                    var timeoutException = exception as TaskTimeoutException <ModelResponse>;
                    if (timeoutException != null)
                    {
                        // TODO(HonzaS): handle this. Wait for a while and then request a new full model state.
                        Log.Error(timeoutException, "Model request timed out");
                    }
                    else
                    {
                        // Keep trying for now. TODO(Premek): Do something smarter...
                        Log.Error(exception, "Model retrieval failed");
                    }

                    m_getFullModel = true;
                }
            }
        }