Add timeout to livetile code? - c#

First post here, sorry for starting with asking questions.
In my Windows Phone 7 app I have a working livetile that is beeing triggered by a background agent. But how can I modify the code so the httpwebrequest timeouts after 10 seconds?
Thanks in advance.
protected override void OnInvoke(ScheduledTask task)
{
//TODO: Add code to perform your task in background
var request = (HttpWebRequest)WebRequest.Create(
new Uri("site.com"));
request.BeginGetResponse(r =>
{
var httpRequest = (HttpWebRequest)r.AsyncState;
var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(r);
using (var reader = new StreamReader(httpResponse.GetResponseStream()))
{
var response = reader.ReadToEnd();
Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
{
string strResult = response;
/// If application uses both PeriodicTask and ResourceIntensiveTask
if (task is PeriodicTask)
{
// Execute periodic task actions here.
ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString().Contains("TileID=2"));
if (TileToFind != null)
{
StandardTileData NewTileData = new StandardTileData
{
BackgroundImage = new Uri("Pin-to-start.png", UriKind.Relative),
Title = strResult,
Count = null
};
TileToFind.Update(NewTileData);
}
}
else
{
// Execute resource-intensive task actions here.
}
NotifyComplete();
}));
}
}, request);
}

Here is copy/paste from code that i use in one of my apps. It will abort the connection after 60 seconds.
private static void DoWebRequest(string uri)
{
string id = "my_request";
Timer t = null;
int timeout = 60; // in seconds
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Accept = "*/*";
request.AllowAutoRedirect = true;
// disable caching.
request.Headers["Cache-Control"] = "no-cache";
request.Headers["Pragma"] = "no-cache";
t = new Timer(
state =>
{
if (string.Compare(state.ToString(), id, StringComparison.InvariantCultureIgnoreCase) == 0)
{
logger.Write("Timeout reached for connection [{0}], aborting download.", id);
request.Abort();
t.Dispose();
}
},
id,
timeout * 1000,
0);
request.BeginGetResponse(
r =>
{
try
{
if (t != null)
{
t.Dispose();
}
// your code for processing the results
}
catch
{
// error handling.
}
},
request);
}
catch
{
}
}

But how can I modify the code so the httpwebrequest timeouts after 10 seconds?
You mean so it'll call NotifyComplete() regardless of timeouts?-) The catch is that after 15 seconds the task terminates, and gets disabled until it's re-launched by the user (inside your app).
I would recommend using TPL for Silverlight and utilizing the ability to use Tasks for setting a Timeout.
Something like:
protected override void OnInvoke(ScheduledTask task)
{
var fetchTask = FetchData(TimeSpan.FromSeconds(10));
fetchTask.ContinueWith(x =>
{
Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
{
string strResult = x.Result; // mind you, x.Result will be "null" when a timeout occours.
...
NotifyComplete();
}));
});
}
private Task<string> FetchData(TimeSpan timeout)
{
var tcs = new TaskCompletionSource<string>();
var request = (HttpWebRequest)WebRequest.Create(new Uri("site.com"));
Timer timer = null;
timer = new Timer(sender =>
{
tcs.TrySetResult(null);
timer.Dispose();
}, null, (int)timeout.TotalMilliseconds, Timeout.Infinite);
request.BeginGetResponse(r =>
{
var httpRequest = (HttpWebRequest)r.AsyncState;
var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(r);
using (var reader = new StreamReader(httpResponse.GetResponseStream()))
{
var response = reader.ReadToEnd();
tcs.TrySetResult(response);
}
});
return tcs.Task;
}

Related

HTTPCliend Handler on Status code 401 block other Tasks

This is code from my handler:
public class MoeZdravjeStatusCodeHandler : DelegatingHandler
{
public MoeZdravjeStatusCodeHandler(HttpMessageHandler innerHandler) : base(innerHandler) { }
public IMojLekDataStore<Object> MojLekDataStore => DependencyService.Get<IMojLekDataStore<Object>>();
public bool flag=true;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = new HttpResponseMessage();
response = await base.SendAsync(request, cancellationToken);
int status_code = (int)response.StatusCode;
if (response.IsSuccessStatusCode)
{
return response;
}
else if(status_code == 401 && DependencyService.Get<ISharedFunctions>().GetUserValidation() == true )
{
try
{
if (flag)
{
flag = false;
string date = DateTime.Today.ToString("yyyy-MM-dd") + "MoeZdravje";
string password = Helpers.ServiceHelper.HashPassword(date).ToString();
LoginMoeZdravje log = new LoginMoeZdravje();
log.EZBO = DependencyService.Get<ISharedFunctions>().GetPatient().Ezbo.ToString();
log.PASSWORD = password;
log.CONFIRMED_USER = 1;
var response_token = await MojLekDataStore.GetMoeZdravjeToken(log);
if (response_token != null && !String.IsNullOrEmpty(response_token.Token))
{
flag = true;
DependencyService.Get<ISharedFunctions>().SaveMoeZdravjeToken(response_token.Token);
await Application.Current.MainPage.DisplayAlert("", "ВАШИОТ ТОКЕН Е ОСВЕЖЕН", AppResources.BTNOK);
Application.Current.MainPage = new NavigationPage(new MainMenu());
}
else
{
flag = true;
await Application.Current.MainPage.DisplayAlert("", AppResources.SERVER_ERROR, AppResources.BTNOK);
}
}
else
{
CancellationTokenSource source = new CancellationTokenSource();
cancellationToken = source.Token;
source.Cancel();
source.Dispose();
}
}
catch (AggregateException ae)
{
foreach (Exception e in ae.InnerExceptions)
{
if (e is TaskCanceledException)
Console.WriteLine("Unable to compute mean: {0}",
((TaskCanceledException)e).Message);
else
Console.WriteLine("Exception: " + e.GetType().Name);
}
}
finally
{
}
}
return response;
}
I want when come to await MojLekDataStore.GetToken(log); block every async Task until finish this request because with this request i get a new token from my Api and need to save that token and call the requests to get the data with the new token. I have a tabbedPage with 4 async Tasks and this handler called two times for get a new token but i need one and to start work with the new token.
EDIT
I added CancellationTokenSource source = new CancellationTokenSource();
but i dont know if this could stop other 3 async task ? The flag is used when first 401status_code request come .

Kafka consume message and then produce to another topic

I have to consume from a Kafka topic, get the message and do some json clean and filter job, then I need to produce the new message to another Kafka topic, my code is like this:
public static YamlMappingNode configs;
public static void Main(string[] args)
{
using (var reader = new StreamReader(Path.Combine(Directory.GetCurrentDirectory(), ".gitlab-ci.yml")))
{
var yaml = new YamlStream();
yaml.Load(reader);
//find variables
configs = (YamlMappingNode)yaml.Documents[0].RootNode;
configs = (YamlMappingNode)configs.Children.Where(k => k.Key.ToString() == "variables")?.FirstOrDefault().Value;
}
CancellationTokenSource cts = new CancellationTokenSource();
Console.CancelKeyPress += (_, e) => {
e.Cancel = true; // prevent the process from terminating.
cts.Cancel();
};
Run_ManualAssign(configs, cts.Token);
}
public static async void Run_ManualAssign(YamlMappingNode configs, CancellationToken cancellationToken)
{
var brokerList = configs.Where(k => k.Key.ToString() == "kfk_broker")?.FirstOrDefault().Value.ToString();
var topics = configs.Where(k => k.Key.ToString() == "input_kfk_topic")?.FirstOrDefault().Value.ToString();
var config = new ConsumerConfig
{
// the group.id property must be specified when creating a consumer, even
// if you do not intend to use any consumer group functionality.
GroupId = new Guid().ToString(),
BootstrapServers = brokerList,
// partition offsets can be committed to a group even by consumers not
// subscribed to the group. in this example, auto commit is disabled
// to prevent this from occurring.
EnableAutoCommit = true
};
using (var consumer =
new ConsumerBuilder<Ignore, string>(config)
.SetErrorHandler((_, e) => Console.WriteLine($"Error: {e.Reason}"))
.Build())
{
//consumer.Assign(topics.Select(topic => new TopicPartitionOffset(topic, 0, Offset.Beginning)).ToList());
consumer.Assign(new TopicPartitionOffset(topics, 0, Offset.End));
//var producer = new ProducerBuilder<Null, string>(config).Build();
try
{
while (true)
{
try
{
var consumeResult = consumer.Consume(cancellationToken);
/// Note: End of partition notification has not been enabled, so
/// it is guaranteed that the ConsumeResult instance corresponds
/// to a Message, and not a PartitionEOF event.
//filter message
var result = ReadMessage(configs, consumeResult.Message.Value);
//send to kafka topic
await Run_ProducerAsync(configs, result);
}
catch (ConsumeException e)
{
Console.WriteLine($"Consume error: {e.Error.Reason}");
}
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Closing consumer.");
consumer.Close();
}
}
}
#endregion
#region Run_Producer
public static async Task Run_ProducerAsync(YamlMappingNode configs, string message)
{
var brokerList = configs.Where(k => k.Key.ToString() == "kfk_broker")?.FirstOrDefault().Value.ToString();
var topicName = configs.Where(k => k.Key.ToString() == "target_kafka_topic")?.FirstOrDefault().Value.ToString();
var config = new ProducerConfig {
BootstrapServers = brokerList,
};
using (var producer = new ProducerBuilder<Null, string>(config).Build())
{
try
{
/// Note: Awaiting the asynchronous produce request below prevents flow of execution
/// from proceeding until the acknowledgement from the broker is received (at the
/// expense of low throughput).
var deliveryReport = await producer.ProduceAsync(topicName, new Message<Null, string> { Value = message });
producer.Flush(TimeSpan.FromSeconds(10));
Console.WriteLine($"delivered to: {deliveryReport.TopicPartitionOffset}");
}
catch (ProduceException<string, string> e)
{
Console.WriteLine($"failed to deliver message: {e.Message} [{e.Error.Code}]");
}
}
}
#endregion
Am I doing something wrong here? The program existed immediately when executing var deliveryReport = await producer.ProduceAsync(topicName, new Message<Null, string> { Value = message });, no error message, no error code.
In the meanwhile I used Python and config the same for Producer, it works well.
Run_ManualAssign(configs, cts.Token);
For this line in the Main function, you are calling async without await in a sync function. Thus the program exit immediately after this invoke started (not finished as it is async)
You could have 2 options
Use async Main function and add await in front of this invoke.
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-7.1/async-main
If you really want to call async function in sync function
Run_ManualAssign(configs, ts.Token).ConfigureAwait(false).GetAwaiter().GetResult();
I solved this problem but I don't know why actually. I opened an issue here.

Problems working with async Task and Textbox.Text = "Hello"

First of all, sorry because I am so new at C# and I decided to make this question because I have been choked in this for hours.
I have an GUI that works with Google Cloud Speech services and make a Speech-to-Text operation. I share with you the whole method that runs when a button is clicked:
private async Task<object> StreamingMicRecognizeAsync(int seconds)
{
if (NAudio.Wave.WaveIn.DeviceCount < 1)
{
Console.WriteLine("No microphone!");
return -1;
}
GoogleCredential googleCredential;
using (Stream m = new FileStream(#"..\..\credentials.json", FileMode.Open))
googleCredential = GoogleCredential.FromStream(m);
var channel = new Grpc.Core.Channel(SpeechClient.DefaultEndpoint.Host,
googleCredential.ToChannelCredentials());
var speech = SpeechClient.Create(channel);
var streamingCall = speech.StreamingRecognize();
// Write the initial request with the config.
await streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
StreamingConfig = new StreamingRecognitionConfig()
{
Config = new RecognitionConfig()
{
Encoding =
RecognitionConfig.Types.AudioEncoding.Linear16,
SampleRateHertz = 48000,
LanguageCode = "es-ES",
},
InterimResults = true,
}
});
// Read from the microphone and stream to API.
object writeLock = new object();
bool writeMore = true;
var waveIn = new NAudio.Wave.WaveInEvent();
waveIn.DeviceNumber = 0;
waveIn.WaveFormat = new NAudio.Wave.WaveFormat(48000, 1);
waveIn.DataAvailable +=
(object sender, NAudio.Wave.WaveInEventArgs args) =>
{
lock (writeLock)
{
if (!writeMore) return;
streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
AudioContent = Google.Protobuf.ByteString
.CopyFrom(args.Buffer, 0, args.BytesRecorded)
}).Wait();
}
};
// Print responses as they arrive.
Task printResponses = Task.Run(async () =>
{
while (await streamingCall.ResponseStream.MoveNext(default(CancellationToken)))
{
foreach (var result in streamingCall.ResponseStream
.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
Console.WriteLine(alternative.Transcript);
//Textbox1.Text = alternative.Transcript;
}
}
}
});
waveIn.StartRecording();
Console.WriteLine("Speak now.");
Result_Tone.Text = "Speak now:\n\n";
await Task.Delay(TimeSpan.FromSeconds(seconds));
// Stop recording and shut down.
waveIn.StopRecording();
lock (writeLock) writeMore = false;
await streamingCall.WriteCompleteAsync();
await printResponses;
return 0;
}
My problem is that I want to update the content of the Textbox1control but it doesn´t work. It writes perfectly the output into the console with the line Console.WriteLine(alternative.Transcript); but not into my textbox.
If someone could help I would appreciate so much his help.
The problem is that you're using Task.Run, which means your code will be running on a thread-pool thread.
Instead of calling Task.Run(), just move that code into a separate async method:
async Task DisplayResponses(IAsyncEnumerator<StreamingRecognizeResponse> responses)
{
while (await responses.MoveNext(default(CancellationToken)))
{
foreach (var result in responses.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
Textbox1.Text = alternative.Transcript;
}
}
}
}
Then call that method directly (without Task.Run) from code that's already on the UI thread (e.g. an event handler).
The async machinery will make sure that after the await expression, you're back on the UI thread (the same synchronization context). So the assignment to the Text property will occur on the UI thread, and all should be well.
For example:
// This would be registered as the event handler for a button
void HandleButtonClick(object sender, EventArgs e)
{
var stream = client.StreamingRecognize();
// Send the initial config request
await stream.WriteAsync(...);
// Presumably you want to send audio data...
StartSendingAudioData(stream);
await DisplayResponses(stream.ResponseStream);
}
Tasks run on seperate threads, so you must Invoke an action that will be performed on the control's thread
Textbox1.Invoke(new Action(() =>
{
Textbox1.Text= "";
}));
Edit: For WPF, I believe the equivalent is
Textbox1.Dispatcher.Invoke(new Action(() =>
{
Textbox1.Text= "";
}));
have you tried using Dispatcher.InvokeASync()?
await Dispatcher.InvokeAsync(() => {while (await streamingCall.ResponseStream.MoveNext(default(CancellationToken)))
{
foreach (var result in streamingCall.ResponseStream
.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
Textbox1.Text = alternative.Transcript;
}
}
}});

UI Frozen / Tried to work with task / async await

I write a WPF Programm which reads the configuration of the PC.
The Programm has to be refreshed an the HttpWebRequest will delay the code so the UI will freez.
I tried to work with async / await tasks.
Method in Class Network:
public TreeView CreatTVURLs()
{
TreeView TVURLs = new TreeView(); //Here it will break. Says no STA-Thread
List<CPingables> lURLs = new List<CPingables>();
lURLs = ReadURLsFromFile();
TVURLs.Name = "URLs";
TVURLs.Background = System.Windows.Media.Brushes.Transparent;
TVURLs.BorderThickness = new Thickness(0);
foreach (CPingables item in lURLs)
{
tviURL.Items.Add("IP:\t\t\t" + item.IP.ToString());
tviURL.Items.Add("URL:\t\t\t" + item.URL);
... more stuff
Tried with Thread:
Code in MainWindow:
public async void openWindow()
{
TreeView tvURLs = new TreeView();
tvURLs = (TreeView) TreeViewTest();
//tvURLs = Network.CreatTVURLs();
StackPanel.Children.Add(tvURLs);
}
private TreeView TreeViewTest()
{
TreeView tvURLs = new TreeView();
Thread t = new Thread(() => { tvURLs = Network.CreatTVURLs(); });
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
return tvURLs;
}
Tried with Task:
public async void openWindow()
{
TreeView tvURLs = new TreeView();
tvURLs = (TreeView) await TreeViewTest();
Task.WaitAll();
StackPanel.Children.Add(tvURLs);
}
private Task<TreeView> TreeViewTest()
{
TreeView tv = new TreeView();
return Task.Factory.StartNew(() => { tv=(TreeView)Network.CreatTVURLs()});
}
I get always says it must me a STA Thread to handle Formitems.
Edit:
HttpWebRequest
public async void pingIP()
{
try
{
if (_IPPingAvailible == true)
{
Ping pinger = new Ping();
PingReply replyIP = pinger.Send(_IP);
_PingByIP = replyIP.Status == IPStatus.Success;
}
}
catch (Exception)
{
_PingByIP = false;
}
try
{
if (_UrlAvailible == true)
{
var uriBuilder = new UriBuilder(_URL);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uriBuilder.Uri);
request.Timeout = 1000;
using (var response = await request.GetResponseAsync() as HttpWebResponse)
{
if (response != null && response.StatusCode == HttpStatusCode.OK)
{
_ReachableByHttp = true;
}
else
{
_ReachableByHttp = false;
}
}
}
}
catch
{
_ReachableByHttp = false;
}
}
The rule is, that you can access UI elements only in UI thread (called also Dispatcher thread, or main thread). You are breaking this rule. You have cerate new thread by:
Task.Factory.StartNew(...) resp by new Thread(...);, but you are manipulating the UI elements (treeview) inside the new thread.
Instead, you should perform only the I/O operation or HttpRequest/Response in the new thread, but everything else (e.g. creating treeview from the http response data) should be done in UI thread.
public async void openWindow()
{
TreeView tvURLs = await Network.CreatTVURLsAsync();
StackPanel.Children.Add(tvURLs);
}
public async Task<TreeView> CreatTVURLsAsync()
{
//you are in dispatcher thread here, so you can access UI elements here
TreeView TVURLs = new TreeView();
TVURLs.Name = "URLs";
TVURLs.Background = System.Windows.Media.Brushes.Transparent;
TVURLs.BorderThickness = new Thickness(0);
List<CPingables> lURLs = null;
await Task.Run(() =>
{
//you are in new thread now, so you cannot access UI elements here
lURL = ReadURLsFromFile();
});
//you are in dispatcher thread again, so you can access UI elements again
foreach (CPingables item in lURLs)
{
var tviURL = new TreeViewItem();
..more stuff
TVURLs.Items.Add(tviURL);
}
}

Live Tiles not Updating

I'm trying to create a background agent that periodically updates a user's Live Tiles on Windows Phone.
Currently, my code for the agent is:
string where = "";
private GeoCoordinate MyCoordinate = null;
HttpWebResponse webResponse;
...
protected override void OnInvoke(ScheduledTask task)
{
System.Diagnostics.Debug.WriteLine("Invoked");
findMe();
NotifyComplete();
}
private void ResponseCallback(IAsyncResult asyncResult)
{
HttpWebRequest webRequest = (HttpWebRequest)asyncResult.AsyncState;
webResponse = (HttpWebResponse)webRequest.EndGetResponse(asyncResult);
MemoryStream tempStream = new MemoryStream();
webResponse.GetResponseStream().CopyTo(tempStream);
}
private async void findMe()
{
Geolocator geolocator = new Geolocator();
geolocator.DesiredAccuracy = PositionAccuracy.High;
try
{
Geoposition currentPosition = await geolocator.GetGeopositionAsync(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10));
MyCoordinate = new GeoCoordinate(currentPosition.Coordinate.Latitude, currentPosition.Coordinate.Longitude);
// var uri = new Uri("http://www.streetdirectory.com//api/?mode=nearby&act=location&output=json&callback=foo&start=0&limit=1&country=sg&profile=template_1&x=" + MyCoordinate.Longitude + "&y=" + MyCoordinate.Latitude + "&dist=1");
// var client = new HttpClient();
var webRequest = (HttpWebRequest)HttpWebRequest.CreateHttp("http://www.streetdirectory.com//api/?mode=nearby&act=location&output=json&callback=foo&start=0&limit=1&country=sg&profile=template_1&x=" + MyCoordinate.Longitude + "&y=" + MyCoordinate.Latitude + "&dist=1");
webRequest.BeginGetResponse(new AsyncCallback(ResponseCallback), webRequest);
System.Diagnostics.Debug.WriteLine("findMe after response");
System.Diagnostics.Debug.WriteLine(MyCoordinate.Latitude);
System.Diagnostics.Debug.WriteLine(MyCoordinate.Longitude);
// var response = await client.GetStringAsync(uri);
System.Diagnostics.Debug.WriteLine(webResponse.ToString());
JToken token = JArray.Parse(webResponse.ToString())[0];
// JToken token = JArray.Parse(response)[0];
var name = token.Next.First.First;
var address = token.Next.Last.First;
where = name + ", " + address;
}
catch (Exception)
{
System.Diagnostics.Debug.WriteLine("findMe died");
where = "";
}
System.Diagnostics.Debug.WriteLine("findMe complete");
UpdateAppTile();
}
private void UpdateAppTile()
{
System.Diagnostics.Debug.WriteLine("UpdateAppTile");
ShellTile appTile = ShellTile.ActiveTiles.First();
if (appTile != null)
{
StandardTileData tileData = new StandardTileData
{
BackContent = where
};
appTile.Update(tileData);
}
System.Diagnostics.Debug.WriteLine("Update Completed: " + where);
}
When I attempt to run this, the code reaches webRequest.BeginGetResponse and subsequently stops. The next line, and ResponseCallback are not reached.
An older version of my code is commented out, which I thought was the problem but it experienced the same problem as well.
The problem is that you are calling NotifyComplete() before the callback returns.
By calling NotifyComplete you're telling the OS that you've finished all your work and the agent can be terminated. Obviously this isn't the case when you're waiting for your webrequest callback.
The simple solution is to move this call into the callback method. obviously you'll need to handle error exceptions and the request timeout taking longer than the agent will wait for as well though.
Changing to using awaitable code may make this easier for you.

Categories