I am trying to do a GATT write operation in a BLE after Notification where I am getting the value from server. The write operation works fine when there is no prior GATT operation like Notification. So this is the code where I am reading the value through notification
public async Task ReadConfigData(ICharacteristic characteristics)
{
if (characteristics != null)
{
try
{
await _adapter.ConnectToDeviceAsync(_device);
characteristics.ValueUpdated += (o, e) =>
{
Device.BeginInvokeOnMainThread(async () =>
{
//var readvalue2 = characteristics.Value;
var bytes = e.Characteristic.Value;
//var readvalue = await characteristics.ReadAsync();
BLEresultnew = System.Text.Encoding.UTF8.GetString(bytes);
Console.WriteLine(BLEresultnew);
//if(BLEresultnew.Contains("start"))
//{
concat += BLEresultnew;
//}
});
};
await characteristics.StartUpdatesAsync();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
This is how I am doing the write operation
public async task WriteDataAsync(String data)
{
if (_characteristicsBLE != null)
{
try
{
// await _adapter.ConnectToDeviceAsync(_device);
byte[] senddata = Encoding.UTF8.GetBytes(data);
int start = 0;
while (start < senddata.Length)
{
int chunkLength = Math.Min(20, senddata.Length - start);
byte[] chunk = new byte[chunkLength];
Array.Copy(senddata, start, chunk, 0, chunkLength);
Device.BeginInvokeOnMainThread(async () =>
{
await Task.Delay(300);
await _characteristicsBLE.WriteAsync(chunk);
});
start += 20;
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
I have even called the write operation in main thread to avoid any threading issue. But still I get GATT error. I have no clue how to fix this any suggestions?
Related
Good day.
I'm having a problem exiting a task with the cancellation token.
My program freezes when I get to the token2.ThrowIfCancellationRequested();.
Following it with the breakpoints is shows that the token2 is cancelled, but the program doesn't revert back to the previous sub routine where I try and catch
try
{
Task.Run(() => SendData_DoWork(_tokenSource3));
}
catch (OperationCanceledException ex)
{
SetText("Communivation error with device");
SetText("");
}
finally
{
token.Dispose();
}
}//comms routine
//send Meter Address to communicate to meter
private void SendData_DoWork(CancellationTokenSource token)
{
var token2 = token.Token;
var _tokenSource4 = new CancellationTokenSource();
try
{
timer.Interval = 10000;
timer.Start();
timer.Elapsed += OnTimerElapsed;
NetworkStream stream = client.GetStream();
SerialConverter serialConverter = new SerialConverter();
Thread.Sleep(1000);
string newtext = null;
newtext = $"/?{address}!\r\n";
SetText("TX: " + newtext);
byte[] newData = stringSend(newtext);
stream.Write(newData, 0, newData.Length);
Thread.Sleep(50);
byte[] message = new byte[23];
int byteRead;
while (true)
{
byteRead = 0;
try
{
byteRead = stream.Read(message, 0, 23);
if (message[0] == (char)0x15)
{
token.Cancel();
}
}
catch
{
token.Cancel();
}
if ((byteRead == 0))
{
token.Cancel();
}
timer.Stop();
timer.Dispose();
ASCIIEncoding encoder = new ASCIIEncoding();
string newresponse = encoder.GetString(serialConverter.convertFromSerial(message));
SetText("RX: " + newresponse);
if (newresponse[0].ToString() == SOH)
{
token.Cancel();
}
if (newresponse != null)
{
/* NEXT SUB ROUTINE*/
}
else { break; }
}//while looop
}//try
catch (Exception ex)
{
token.Cancel();
}
if (token2.IsCancellationRequested)
{
timer.Stop();
timer.Dispose();
token2.ThrowIfCancellationRequested();
}
}//sendData subroutine
You are launching a Task, and ignoring the result; the only time Task.Run would throw is if the task-method is invalid, or enqueuing the operation itself failed. If you want to know how SendData_DoWork ended, you'll need to actually check the result of the task, by capturing the result of Task.Run and awaiting it (preferably asynchronously, although if we're talking async, SendData_DoWork should probably also be async and return a Task).
Your catch/finally will probably be exited long before SendData_DoWork even starts - again: Task.Run just takes the time required to validate and enqueue the operation; not wait for it to happen.
I think you have missunderstood how cancellation tokens are supposed to work. Your work method should take a CancellationToken, not a CancellationTokenSource. And it should call ThrowIfCancellationRequested inside the loop, not after. I would suspect that you would get some issues with multiple cancel calls to the same cancellation token.
Typically you would use a pattern something like like this:
public void MyCancelButtonHandler(...) => cts.Cancel();
public async void MyButtonHandler(...){
try{
cts = new CancellationTokenSource(); // update shared field
await Task.Run(() => MyBackgroundWork(cts.Token));
}
catch(OperationCancelledException){} // Ignore
catch(Exception){} // handle other exceptions
}
private void MyBackgroundWork(CancellationToken cancel){
while(...){
cancel.ThrowIfCancellationRequested();
// Do actual work
}
}
So in my particular case it seems like changing the sub-routines from private async void ... to private async Task fixes the particular issue that I'm having.
I implemented Task synchronization using Monitor in C#.
However, I have read Monitor should not be used in asynchronous operation.
In the below code, how do I implement Monitor methods Wait and PulseAll with a construct that works with Task (asynchronous operations).
I have read that SemaphoreSlim.WaitAsync and Release methods can help.
But how do they fit in the below sample where multiple tasks need to wait on a lock object, and releasing the lock wakes up all waiting tasks ?
private bool m_condition = false;
private readonly Object m_lock = new Object();
private async Task<bool> SyncInteralWithPoolingAsync(
SyncDatabase db,
List<EntryUpdateInfo> updateList)
{
List<Task> activeTasks = new List<Task>();
int addedTasks = 0;
int removedTasks = 0;
foreach (EntryUpdateInfo entryUpdateInfo in updateList)
{
Monitor.Enter(m_lock);
//If 5 tasks are waiting in ProcessEntryAsync method
if(m_count >= 5)
{
//Do some batch processing to obtian values to set for adapterEntry.AdapterEntryId in ProcessEntryAsync
//.......
//.......
m_condition = true;
Monitor.PulseAll(m_lock); // Wakes all waiters AFTER lock is released
}
Monitor.Exit(m_lock);
removedTasks += activeTasks.RemoveAll(t => t.IsCompleted);
Task processingTask = Task.Run(
async () =>
{
await this.ProcessEntryAsync(
entryUpdateInfo,
db)
.ContinueWith(this.ProcessEntryCompleteAsync)
.ConfigureAwait(false);
});
activeTasks.Add(processingTask);
addedTasks++;
}
}
private async Task<bool> ProcessEntryAsync(SyncDatabase db, EntryUpdateInfo entryUpdateInfo)
{
SyncEntryAdapterData adapterEntry =
updateInfo.Entry.AdapterEntries.FirstOrDefault(e => e.AdapterId == this.Config.Id);
if (adapterEntry == null)
{
adapterEntry = new SyncEntryAdapterData()
{
SyncEntry = updateInfo.Entry,
AdapterId = this.Config.Id
};
updateInfo.Entry.AdapterEntries.Add(adapterEntry);
}
m_condition = false;
Monitor.Enter(m_lock);
while (!m_condition)
{
m_count++;
Monitor.Wait(m_lock);
}
m_count--;
adapterEntry.AdapterEntryId = .... //Set Value obtained form batch processing
Monitor.Exit(m_lock);
}
private void ProcessEntryCompleteAsync(Task<bool> task, object context)
{
EntryProcessingContext ctx = (EntryProcessingContext)context;
try
{
string message;
if (task.IsCanceled)
{
Logger.Warning("Processing was cancelled");
message = "The change was cancelled during processing";
}
else if (task.Exception != null)
{
Exception ex = task.Exception;
Logger.Warning("Processing failed with {0}: {1}", ex.GetType().FullName, ex.Message);
message = "An error occurred while synchronzing the changed.";
}
else
{
message = "The change was successfully synchronized";
if (task.Result)
{
//Processing
//...
//...
}
}
}
catch (Exception e)
{
Logger.Info(
"Caught an exception while completing entry processing. " + e);
}
finally
{
}
}
Thanks
I would like to know if the code I produced is good practice and does not produce leaks, I have more than 7000 objects Participant which I will push individually and Handle the Response to save the "external" id in our database. First I use the Parallel ForEach on the list pPartcipant:
Parallel.ForEach(pParticipant, participant =>
{
try
{
//Create
if (participant.id == null)
{
ExecuteRequestCreate(res, participant);
}
else
{//Update
ExecuteRequestUpdate(res, participant);
}
}
catch (Exception ex)
{
LogHelper.Log("Fail Parallel ", ex);
}
});
Then I do a classic (not async request), but after I need to "handle" the response (print in the console, save in a text file in async mode, and Update in my database)
private async void ExecuteRequestCreate(Uri pRes, ParticipantDo pParticipant)
{
try
{
var request = SetRequest(pParticipant);
//lTaskAll.Add(Task.Run(() => { ExecuteAll(request, pRes, pParticipant); }));
//Task.Run(() => ExecuteAll(request, pRes, pParticipant));
var result = RestExecute(request, pRes);
await HandleResult(result, pParticipant);
//lTaskHandle.Add(Task.Run(() => { HandleResult(result, pParticipant); }));
}
catch (Exception e)
{
lTaskLog.Add(LogHelper.Log(e.Message + " " + e.InnerException));
}
}
Should I run a new task for handeling the result (as commented) ? Will it improve the performance ?
In comment you can see that I created a list of tasks so I can wait all at the end (tasklog is all my task to write in a textfile) :
int nbtask = lTaskHandle.Count;
try
{
Task.WhenAll(lTaskHandle).Wait();
Task.WhenAll(lTaskLog).Wait();
}
catch (Exception ex)
{
LogHelper.Log("Fail on API calls tasks", ex);
}
I don't have any interface it is a console program.
I would like to know if the code I produced is good practice
No; you should avoid async void and also avoid Parallel for async work.
Here's a similar top-level method that uses asynchronous concurrency instead of Parallel:
var tasks = pParticipant
.Select(participant =>
{
try
{
//Create
if (participant.id == null)
{
await ExecuteRequestCreateAsync(res, participant);
}
else
{//Update
await ExecuteRequestUpdateAsync(res, participant);
}
}
catch (Exception ex)
{
LogHelper.Log("Fail Parallel ", ex);
}
})
.ToList();
await Task.WhenAll(tasks);
And your work methods should be async Task instead of async void:
private async Task ExecuteRequestCreateAsync(Uri pRes, ParticipantDo pParticipant)
{
try
{
var request = SetRequest(pParticipant);
var result = await RestExecuteAsync(request, pRes);
await HandleResult(result, pParticipant);
}
catch (Exception e)
{
LogHelper.Log(e.Message + " " + e.InnerException);
}
}
I've an existing code I wrote some time ago, that works but I dislike the fact that the thread I start remains in loop.
This piece of code is a consumer on an IBMMQ code, waiting for messages to be processed.The problem I've is that with the following code
private Task ExecuteQueuePolling(CancellationToken cancellationToken)
{
return Task.Factory.StartNew(() =>
{
ConnectToAccessQueue();
Logger.Debug($"Accessed to the queue {queueName}");
Logger.DebugFormat("Repeating timer started, checking frequency: {checkingFrequency}",
checkingFrequency);
while (!cancellationToken.IsCancellationRequested)
{
Logger.Trace( () => "Listening on queues for new messages");
// isChecking = true;
var mqMsg = new MQMessage();
var mqGetMsgOpts = new MQGetMessageOptions
{ WaitInterval = (int)checkingFrequency.TotalMilliseconds };
// 15 second limit for waiting
mqGetMsgOpts.Options |= MQC.MQGMO_WAIT | MQC.MQGMO_FAIL_IF_QUIESCING |
MQC.MQCNO_RECONNECT_Q_MGR | MQC.MQOO_INPUT_AS_Q_DEF;
try
{
mqQueue.Get(mqMsg, mqGetMsgOpts);
if (string.Compare(mqMsg.Format, MQC.MQFMT_STRING, StringComparison.Ordinal) == 0)
{
var text = mqMsg.ReadString(mqMsg.MessageLength);
Logger.Debug($"Message received : [{text}]");
Message message = new Message { Content = text };
foreach (var observer in observers)
observer.OnNext(message);
}
else
{
Logger.Warn("Non-text message");
}
}
catch (MQException ex)
{
if (ex.Message == MQC.MQRC_NO_MSG_AVAILABLE.ToString())
{
Logger.Trace("No messages available");
//nothing to do, emtpy queue
}
else if (ex.Message == MQC.MQRC_CONNECTION_BROKEN.ToString())
{
Logger.ErrorException("MQ Exception, trying to recconect", ex);
throw new ReconnectException();
}
}
Thread.Sleep(100);
}
},cancellationToken);
}
//Calling method
try
{
string queueManagerName = configuration.GetValue<string>("IBMMQ:QUEUE_MANAGER_NAME");
// var queueManager = new MQQueueManager(queueManagerName,dictionary2);
QueueMonitor monitor = new QueueMonitor(configuration, "IMPORTER_RECEIVER_TEST");
//_subscription = monitor.Subscribe(receiver);
await monitor.StartAsync(cts.Token).ConfigureAwait(false);
}
catch (Exception e)
{
log.Error(e, "Error creating the queue monitor or it's subscription");
}
finally
{
WaitForCancel(cts);
}
The call to await monitor.StartAsync(cts.Token).ConfigureAwait(false); remains pending.
How should I modify my code, so that the call returns and in background the task continue to loop?
Thanks in advance
Here is how you can simplify your code by replacing Thread.Sleep with Task.Delay:
private async Task ExecuteQueuePolling(CancellationToken cancellationToken)
{
while (true)
{
// Process mqQueue here
await Task.Delay(100, cancellationToken);
}
}
Task.Delay has the advantage that accepts a CancellationToken, so in case of cancellation the loop will exit immediately. This could be important if the pooling of the MQ was lazier (for example every 5 seconds).
private static Task _runningTask;
static void Main(string[] args)
{
var cts = new CancellationTokenSource();
_runningTask = ExecuteQueuePolling(cts.Token);
WaitForCancel(cts);
}
private static void WaitForCancel(CancellationTokenSource cts)
{
var spinner = new SpinWait();
while (!cts.IsCancellationRequested
&& _runningTask.Status == TaskStatus.Running) spinner.SpinOnce();
}
private static Task ExecuteQueuePolling(CancellationToken cancellationToken)
{
var t = new Task(() =>
{
while (!cancellationToken.IsCancellationRequested)
; // your code
if (cancellationToken.IsCancellationRequested)
throw new OperationCanceledException();
}, cancellationToken, TaskCreationOptions.LongRunning);
t.Start();
return t;
}
This is killing me. I'm new to android/Xamarin. I can't find anything on the web that explains what is going on here.
I'm using xamarin forms for my application. I have a page that synchronizes the device with a web service.
The method simply retrieves 100 records at a time from the web service and updates a sqlite table on the device. I randomly get this error. I'm running 5000 records for my test sample.
Here is the button click:
public async void OnSyncCachedData_Clicked(object sender, EventArgs e)
{
activityIndicatorAll.IsRunning = true;
try
{
actIndSyncItems.IsRunning = true;
SyncAllFinished += SynchAllFinishedProcessing;
await Task.Run(async () => await App.ItemsRepo.LoadCacheDataFromCacheAsync(DbPath).ConfigureAwait(false));
}
catch (BAL.Exceptions.NetworkException nex)
{
await DisplayAlert(Messages.TitleError, nex.Message, Messages.MsgOk);
}
catch (Exception ex)
{
await DisplayAlert(Messages.TitleError, string.Format(Messages.MsgAreYouConnectedParm1, ex.Message), Messages.MsgOk);
}
finally
{
EventHandler handler = SyncAllFinished;
if (handler != null)
{
handler(this, new EventArgs());
}
SyncAllFinished -= SynchAllFinishedProcessing;
}
}
the main worker method:
public async Task<bool> LoadCacheDataFromCacheAsync(string dbPath)
{
WebSvcManagers.ItemsManager itemsWebServiceManager = new WebSvcManagers.ItemsManager();
List<Models.WebServiceItems> consumedRecords = new List<Models.WebServiceItems>() { };
int bufferSize = 100;
Log.Debug(TAG, "LoadCacheDataFromCacheAsync starting");
try
{
{
int lastID = 0;
IEnumerable<Models.WebServiceItems> remoteRecords = await BAL.DataAccessHelper.GetItemsFromGPCacheAsync(App.Login, lastID, bufferSize, itemsWebServiceManager).ConfigureAwait(false);
while (remoteRecords.Count() != 0)
{
foreach (Models.WebServiceItems remoteItem in remoteRecords)
{
// DbActionTypes dbAction = (DbActionTypes)remoteItem.DbAction;
Models.Items itemRecord = new Models.Items() { ItemNumber = remoteItem.ItemNumber.ToUpper().Trim(), Description = remoteItem.Description.Trim() };
Log.Debug(TAG, "Processing {0}", remoteItem.ItemNumber.Trim());
bool success = await AddRecordAsync(itemRecord).ConfigureAwait(false);
if (success)
consumedRecords.Add(remoteItem);
}
lastID = remoteRecords.Max(r => r.RecordID) + 1;
remoteRecords = await BAL.DataAccessHelper.GetItemsFromGPCacheAsync(App.Login, lastID, bufferSize, itemsWebServiceManager).ConfigureAwait(false);
}
}
// await UpdateConsumedRecords(consumedRecords).ConfigureAwait(false);
return true;
}
catch (Exception ex)
{
this.StatusMessage = ex.Message;
Log.Debug(TAG, "Error Catch: {0}", StatusMessage);
return false;
}
finally
{
itemsWebServiceManager = null;
HandleSyncFinished(this, new EventArgs());
SyncAllFinished -= HandleSyncFinished;
}
}
My simple webservice manager:
public static async Task<IEnumerable<Models.WebServiceItems>> GetItemsFromGPCacheAsync(Models.Login login, int offset, int bufferCount, WebSvcManagers.ItemsManager manager)
{
try
{
return await manager.GetCacheRecordsAsync(login, offset, bufferCount).ConfigureAwait(false);
}
catch (Exception)
{
throw;
}
}
And the code for the interaction with the web service:
const int bufferSize = 100;
public async Task<IEnumerable<Models.WebServiceItems>> GetCacheRecordsAsync(Models.Login login, int offSet, int bufferCount)
{
string deviceID = App.ConfigSettings.DeviceID.ToString("D");
try
{
///* Testing start */
//return await DataStore(bufferCount, offSet).ConfigureAwait(false);
///* Testing end */
if (!App.IsConnected)
throw new BAL.Exceptions.NetworkException(Messages.ExceptionNetworkConnection);
string user = login.UserName;
string password = login.Password;
HttpClient client = HttpClientExtensions.CreateHttpClient(user, password);
try
{
List<Models.WebServiceItems> items = new List<Models.WebServiceItems>() { };
int lastID = offSet;
int i = 0;
string uri = string.Format("{0}", string.Format(Messages.WebRequestItemsCacheParms3, deviceID, lastID, Math.Min(bufferCount, bufferSize)));
Log.Debug(TAG, string.Format("Webservice {0}", uri));
string response = await client.GetStringAsync(uri).ConfigureAwait(false);
while (i < bufferCount && response != null && response != "[]")
{
while (response != null && response != "[]")
{
dynamic array = JsonConvert.DeserializeObject(response);
foreach (var item in array)
{
i++;
items.Add(new Models.WebServiceItems()
{
ItemNumber = item["ITEMNMBR"].Value.Trim(),
Description = item["ITEMDESC"].Value.Trim(),
DbAction = (int)(item["DbAction"].Value),
RecordID = (int)(item["DEX_ROW_ID"].Value),
});
lastID = (int)(item["DEX_ROW_ID"].Value);
Log.Debug(TAG, string.Format("Webservice {0}", item["ITEMNMBR"].Value.Trim()));
}
if (i < Math.Min(bufferCount, bufferSize))
{
uri = string.Format("{0}", string.Format(Messages.WebRequestItemsCacheParms3, deviceID, lastID + 1, Math.Min(bufferCount, bufferSize)));
Log.Debug(TAG, string.Format("Webservice {0}", uri));
response = await client.GetStringAsync(uri).ConfigureAwait(false);
}
else
break;
}
}
Log.Debug(TAG, string.Format("Webservice return {0} items", items.Count()));
return items;
}
catch (Exception ex)
{
Log.Debug(TAG, "Error Catch: {0}", ex.Message);
throw ex;
}
}
catch (System.Net.Http.HttpRequestException nex)
{
throw new Exception(string.Format(Messages.ExceptionWebServiceLoginParm1, nex.Message));
}
catch (Exception)
{
throw;
}
}
I've had assistance from a Xamarin instructor and we thought we got it, (because this is random), but it clearly is not swatted. I've been hitting my head up against a wall for over 3 weeks on this. It smells like a memory leak, but I have no idea how to find it with such a generic error message that doesn't appear on web searches.
Somebody out there with some major brains, Please help!
Error:
08-03 12:41:11.281 D/X:ItemsRepositiory(16980): UpdateRecordAsync 65702-40710
08-03 12:41:11.306 D/X:ItemsManager(16980): Webservice DeleteCacheRecordAsync 20497
08-03 12:41:11.406 D/X:ItemsManager(16980): Webservice api/InventoryItems?DeviceID=7c5bb45d-2ea0-45b9-ae50-92f2e25a2983&OffSet=20498&Max=100&cached=true
Thread finished: <Thread Pool> #7 08-03 12:41:11.521 E/art (16980): Nested signal detected - original signal being reported The thread 'Unknown' (0x7) has exited with code 0 (0x0).
This might be a problem with the VS Android Emulator. As we have shown in tests, this is not reproducible on the Google Android Emulators, nor on Xamarin Android Player, nor on a physical Android device.