Xamarin Plugin.BluetoothLE await characteristic notification - xamarin

I would like to write one nice, tidy "login" function that I use to log in to a bluetooth peripheral by sending it a password.
public async Task<bool> LogIn()
{
bool result = false;
//First log in
var connectHook = Device.ConnectHook(BleService.Control, new Guid[] { BleCharacteristic.PasswordResult });
connectHook.Subscribe(ScanResult =>
{
ScanResult.Characteristic.DisableNotifications();
//Succesful Login
if (ScanResult.Data[0] == 1)
{
result = true;
}
//Failed Login
else
{
result = false;
}
});
await Device.WriteCharacteristic(BleService.Control, BleCharacteristic.Password, PasswordBytes);
await connectHook.FirstAsync();
return result;
}
I basically want to subscribe to the password result characteristic, then write the password, and then wait for the password result before returning from the function.
Is this the place to use a promise? I have read about them but do not understand their use.
Thanks
EDIT:
Did some digging and think I've found a good answer. I've updated the code above to reflect it, let me know what you think.
EDIT 2:
So, the code above doesn't work because nothing is returned from
await connectHook.FirstAsync();
If I comment that line out it all works....but it seems like it's working because I have a race condition and the notification gets back before the other code completes.
Does the function magically await for that notification? I thought the observable implemented IEnumerable and would return the first item as long as it had come back? I'm new to this reactive stuff so please be kind.

In you code:
The method connectHook.Subscribe is a asynchronous method. Before you added the two await methods, the function login will directly return false without wait the callback in connectHook.Subscribe,so the return value in login will not be changed whether you login successfully or failed to login.
After you add the two await methods,I guess the callback in connectHook.Subscribe has been triggered after these two methods finished running. So the return value will be changed with the return value in the callback.

Related

Axios async/await flow

I'm dipping my toe into the waters of Axios and async/await at the same time, and am trying to understand something about the control flow. Is the following legitimate?
let loading=true;
(async() => {
let response = null;
try {
response = await axios.get('https://whatever.com/api');
} finally {
loading=false;
}
if(response){
//do something with response here
}
})();
That is, can I count on the request to have returned at the point I am accessing the response variable? I appreciate I could guarantee it is by moving it into the 'try' immediately after the axios get, but then I would have to have the loading=false line before it, as well as in 'finally' (or 'catch'). I need to ensure that loading is set to false before any further actions, whether the request succeeds or fails, and I don't want to repeat myself. Maybe there's a better way of doing this?
Edit
Now that you have changed the question, the previous solution will not be working correctly. The issue is that the code inside the IIFE will be executed after everything else is finished, so loading will never be set to false from the perspective of the outside code. (the other code will be executed, and thеn the IIFE. That's because of the event loop). Your best bet is to make the outside code async and await the axios promise.
If you provide the problem details I might be able to help you refactor it.
Previous answer
I need to ensure that loading is set to false before any further actions
Any code after the await is guaranteed to NOT be loading:
(async() => {
let response = await axios.get('https://whatever.com/api');
// the request is finished, the await guarantees that
})();
If you need error handling, you can wrap it in a try/catch:
(async() => {
try {
let response = await axios.get('https://whatever.com/api');
// definitely not loading
}
catch (e) {
// definitely not loading, but an error occurred
}
})();

JS: Async function with await statement results in undefined promise. What am I doing wrong? Simple example

I'm new to JS and am currently immersing myself in asynchronous functions and promises. Having quite some experience in Python and R, this is totally new for me. I read many different websites, topics and solutions for returning a value in an asynchronous function - but I can't get it to work. Below I boiled it down to a simplification of the function I wrote, designed to get information from google about a location and return it. Simple as that. I followed the advice online and tried to rewrite the following advice from Benjamin Gruenbaum on How do I return the response from an asynchronous call?.
async function foo(){
var data = await fetch("/echo/json"); // notice the await
// code here only executes _after_ the request is done
return data.json(); // data is defined
}
Please see my own code below. It seems to me that it I'm doing the same thing, but it still logs as Promise {<pending>}... What am I doing wrong? data should be an array. Even if I only replace my googleapi url and use fetch() and .json(), it logs as Promise {<pending>}.
async function foo() {
var data = await axios.get("https://maps.googleapis.com/maps/api/geocode/json?address=Amsterdam&key=API_KEY");
return data;
}
console.log(foo())
try This way of calling async function
async function foo()
{
let response = await fetch(`https://maps.googleapis.com/maps/api/geocode/json?address=Amsterdam&key=API_KEY`);
let data = await response.json()
return data;
}
foo().then(data => console.log(data));
Can you try below code to know what is going on in the "data" you got .
This is not the solution of your problem but just you will know what you got in result
console.log(JSON.stringify(foo()));

NetworkStream ReadAsync and WriteAsync hang infinitelly when using CancellationTokenSource - Deadlock Caused by Task.Result (or Task.Wait)

After reading pretty much every question on Stack Overflow and Microsoft's documentation about NetworkStream, I dont understand what is wrong with my code.
The problem I see is that my method GetDataAsync() hangs very often. I call this method from Init Method like so:
public MyView(string id)
{
InitializeComponent();
MyViewModel myViewModel = session.Resolve<MyViewModel>(); //Autofac
myiewModel.Init(id);
BindingContext = myViewModel;
}
Above, my View does its initialization, then resolves MyViewModel from Autofac DiC and then calls MyViewModel Init() method to do some additional setup on the VM.
The Init method then calls my Async method GetDataAsync which return a IList like so:
public void Init()
{
// call this Async method to populate a ListView
foreach (var model in GetDataAsync("111").Result)
{
// The List<MyModel> returned by the GetDataAsync is then
// used to load ListView's ObservableCollection<MyModel>
// This ObservableCollection is data-bound to a ListView in
// this View. So, the ListView shows its data once the View
// displays.
}
}
, and here is my GetDataAsync() method including my comments:
public override async Task<IList<MyModel>> GetDataAsync(string id)
{
var timeout = TimeSpan.FromSeconds(20);
try
{
byte[] messageBytes = GetMessageBytes(Id);
using (var cts = new CancellationTokenSource(timeout))
using (TcpClient client = new TcpClient(Ip, Port))
using (NetworkStream stream = client.GetStream())
{
await stream.WriteAsync(messageBytes, 0, messageBytes.Length, cts.Token);
await stream.FlushAsync(cts.Token);
byte[] buffer = new byte[1024];
StringBuilder builder = new StringBuilder();
int bytesRead = 0;
await Task.Delay(500);
while (stream.DataAvailable) // need to Delay to wait for data to be available
{
bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cts.Token);
builder.AppendFormat("{0}", Encoding.ASCII.GetString(buffer, 0, bytesRead));
}
string msg = buffer.ToString();
}
return ParseMessageIntoList(msg); // parses message into IList<MyModel>
}
catch (OperationCanceledException oce)
{
return await Task.FromResult<IList<RoomGuestModel>>(new List<RoomGuestModel>());
}
catch (Exception ex)
{
return await Task.FromResult<IList<RoomGuestModel>>(new List<RoomGuestModel>());
}
}
I would expect that a ReadAsync or WriteAsync either complete successfully, throw some exception, or get cancelled after 10 seconds in which case I would catch OperationCanceledException.
However, it just hangs endlessly when I call method above. If I am debugging and have some breakpoints in the code above, I will be able to go through the method entirely but if I call it 2nd time, app just hangs forever.
I am new to Tasks and Async programming, so I am also not sure I do my cancellations and exception handling properly here?
UPDATE AND FIX
I figured out how to fix the deadlock issue. In hope this will help others sho might run into the same issue, I'll first explain it. The articles that helped me a lot are:
https://devblogs.microsoft.com/pfxteam/await-and-ui-and-deadlocks-oh-my/ by Stephen Taub
https://montemagno.com/c-sharp-developers-stop-calling-dot-result/ by James Montemagno
https://msdn.microsoft.com/en-us/magazine/jj991977.aspx by StephenCleary
https://blog.xamarin.com/getting-started-with-async-await/ by Jon Goldberger
#StephenCleary was great help understanding the issue. Calling Result or Wait (above, I call Result when calling GetDataAsync) will lead to dead-lock.
The context thread (UI in this case) is now waiting for GetDataAsync to complete, but GetDataAsync captures the current context-thread (UI thread), so it can resume on it once it gets data from TCP. But since this context-thread is now blocked by call to Result, it cannot resume.
The end result is that it looks like call to GetDataAsync has deadlocked but in reality, it is call to Result that deadlocked.
After reading tons of articles from #StephenTaub, #StephenCleary, #JamesMontemagno, #JoeGoldenberger (thank you all), I started getting understanding of the issue (I am new to TAP/async/await).
Then I discovered continuations in Tasks and how to use them to resolve the issue (thanks to Stephen Taub's article above).
So, instead of calling it like:
IList<MyModel> models = GetDataAsync("111").Result;
foeach(var model in models)
{
MyModelsObservableCollection.Add(model);
}
, I call it with continuation like this:
GetDataAsync(id)
.ContinueWith((antecedant) =>
{
foreach(var model in antecedant.Result)
{
MyModelsObservableCollection.Add(model);
}
}, TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith((antecedant) =>
{
var error = antecedant.Exception.Flatten();
}, TaskContinuationOptions.OnlyOnFaulted);
This seam to have fixed my deadlocking issue and now my list will load fine even though it is loaded from the constructor.
So, this seam to work just fine. But #JoeGoldenberger also suggests another solution in his article https://blog.xamarin.com/getting-started-with-async-await/ which is to use Task.Run(async()=>{...}); and inside that await GetDataAsync and load ObservableCollection. So, I gave that a try as well and that is not blocking either, so working great:
Task.Run(async() =>
{
IList<MyModel> models = await GetDataAsync(id);
foreach (var model in models)
{
MyModelsObservableCollection.Add(model);
}
});
So, it looks like either of these 2 will remove deadlock just fine. And since above my Init method is called from a c-tor; therefore, I cannot make it Async and await on this, using one of the 2 methods described above resolves my problem. I dont know which one is better but in my tests, they do work.
Your problem is most likely due to GetDataAsync("111").Result. You shouldn't block on async code.
This can cause deadocks. E.g., if you're on a UI thread, the UI thread will start GetDataAsync and run it until it hits an await. At this point, GetDataAsync returns an incomplete task, and the .Result call blocks the UI thread until that task is completed.
Eventually, the inner async call completes and GetDataAsync is ready to resume executing after its await. By default, await captures its context and resumes on that context. Which in this example is the UI thread. Which is blocked since it called Result. So, the UI thread is waiting for GetDataAsync to complete, and GetDataAsync is waiting for the UI thread so it can complete: deadlock.
The proper solution is to go async all the way; replace .Result with await, and make the necessary changes to other code for that to happen.
As stated in my update, going async all the way by providing an async lambda like below resolved the issue for me
Task.Run(async() =>
{
IList<MyModel> models = await GetDataAsync(id);
foreach (var model in models)
{
MyModelsObservableCollection.Add(model);
}
});
Loading asynchronously an observable collection in a ctor this way (in my case, ctor calls Init which then uses this Task.Run) solves problem

How to remove tooManyAttempts message in Prompt.Choice? How to accept text in Prompt.Choice that is not in list of options? C#

I'm using bot-Framework SDK3 C#.
I want to allow user input anything which is not in "PromptDialog.Choice"'s options. Any better ways to recommend?
This is my code.
private async Task SelectCategory(IDialogContext context)
{
List<string> options = new List<string>();
options = category.Keys.ToList();
options.Add("Category1");
options.Add("Category2");
options.Add("Category3");
PromptOptions<string> promptOptions = new PromptOptions<string>(
prompt: "which one do you prefer?",
tooManyAttempts: "",
options: options,
attempts: 0);
PromptDialog.Choice(context: context, resume: ResumeAfterSelectCategory, promptOptions: promptOptions);
await Task.FromResult<object>(null);
}
private async Task ResumeAfterSelectCategory(IDialogContext context, IAwaitable<string> result)
{
try
{
selected = await result;
}
catch (Exception)
{
// if the user's input is not in the select options, it will come here
}
}
But the problem is it always send the message "tooManyAttempts". If I set it to empty, I will send me "0".
I suppose you are using NodeJS. You can use the simple builder.Prompts.choice with maxRetries set to 0. Here is a sample snippet. It asks user to choose some option from a list, or they can enter something which is not in the list.
If you are using C# SDK, you can find some similar option for the list.
bot.dialog("optionalList", [
function(session){
builder.Prompts.choice(
session,
"Click any button or type something",
["option1", "option2", "option3"],
{maxRetries: 0} // setting maxRetries to zero causes no implicit checking
)
},
function(session, result){
// something from the list has been clicked
if(result.response && result.response.entity){
console.log(result.response.entity); // use the clicked button
} else {
console.log(session.message.text) // if user entered something which is not in the list
}
}
]);
EDIT 1:
Hi, Saw that you are using C# SDK. I am not that proficient with that but I can give you some suggestion.
The list which you generate in the async task SelectCategory you can generate in some other place, which is also accessible to the second async task ResumeAfterSelectCategory, (like making it a class variable or getting from database).
Now that the list is accessible in the 2nd task, you can compare what user has typed against the list to determine if the message is from the list or not.
If message is something from the list, then take action accordingly, otherwise user has entered something which is not in the list, and then take action accordingly.
Your 2nd problem is
And if user typed, it will show a message "you tried to many times"
What is meant by that? Does bot sends "you tried to many times" to the bot visitor. In which case it could be the behavior of library. You will be able to control that only if library provides some option. Else I don't know. Hope, that helps
EDIT 2:
I came across this SO question Can I add custom logic to a Bot Framework PromptDialog for handling invalid answers?
You can use that questions answer. Basically extending PromptDialog.PromptChoice<T>.Here is an example.
Override TryParse method like this
protected override bool TryParse(IMessageActivity message, out T result)
{
bool fromList = base.TryParse(message, out result);
if (fromList)
{
return true;
} else {
// do something here
return true; // signal that parsing was correct
}
}
I used node.js and to get message which user entered. use this code snippet.
(session, args) => {
builder.Prompts.text(session, "Please Enter your name.");
},
(session, args) => {
session.dialogData.username = args.response;
session.send(`Your user name is `${session.dialogData.username}`);
}

React dispatcher WAITFOR

I'm trying to use the waitFor function of react.js but it seems I'm doing something wrong.
What I want to do i basic, wait for a store to be filled before calling it from another store.
1.Register token in the first store
RipplelinesStore.dispatcherIndex= Dispatcher.register(function(payload) {
var action = payload.action;
var result;
switch(action.actionType) {
case Constants.ActionTypes.ASK_RIPPLELINES:
registerAccount(action.result);
RipplelinesStore.emitChange(action.result);
break;
}
});
2.Write the wait for in the other store
Dispatcher.register(function(payload) {
var action = payload.action;
var result;
switch(action.actionType) {
case Constants.ActionTypes.ASK_RIPPLEACCOUNTOVERVIEW:
console.log("overviewstore",payload);
Dispatcher.waitFor([
RipplelinesStore.dispatcherIndex,
]);
RippleaccountoverviewsStore.test= RipplelinesStore.getAll();
console.log(RippleaccountoverviewsStore.test);
break;
}
return true;
});
Unfortunately my getall() method return an empty object (getAll() is well written). So it seems that the waitFor dispatcher function is not working.
Basically I know that's because the first store is still receiving the answer from the server but I thought that waitFor would waitfor it to be fetched I don't get it.
Any clue ? Thanks!
Edit: I fire the first store fetch like tha. What I don't understand is that I'm dispatching the load once my backbone collection has fetched (I dispatch on succeed with a promise...)
ripplelinescollection.createLinesList(toresolve.toJSON()).then(function() {
Dispatcher.handleViewAction({
actionType: Constants.ActionTypes.ASK_RIPPLELINES,
result: ripplelinescollection
});
});
I also tried to bind the waitfor to an action which is never called but the other store is still not waiting ! WEIRD !
seems like the problem is the async fetch from the server. waitFor isn't supposed to work this way. You will have to introduce another action that is triggered as soon as the data has been received from the server.
Have a look at this answer: https://stackoverflow.com/a/27797444/1717588

Resources