Struggling with serial port peripherals and tasks in C#
Currently I have been working on developing, fortunately quite freely, innovative ways to make the company's software, that actually works, better. I think there are many areas of improvement in the code, I feel like having this sixth sense when the code its doing something wrong but have no idea what, like smelling so bad. So i decided to dig deeper into the code to find out what's going on.
Putting it in context
I develop desktop apps in Windows to ATM solutions so I have to work with peripherals, especially bills dispenser, that in specific is headache, 'cause, until recently, it was handled by an Arduino microcontoller and the code was unreadble, so nobody in the team even tried to modify it, even though we knew that the Arduino code was incomplete (it was coded several years ago, and nobody touched it after that). The dispenser works the basic, only dispense the quantity of bills of each denomination required to give the cashback, but if it jams or any denomination cassette run out of bills, sorry you have to wait until we respond to the call for support, I hate to provide support. I realized that the dispenser can resolve it's own jams (in the majority of situations) by itself and we can resolve the empty cassete problem by software, recalculating the cashback and sending the dispend command again with the new quantities, so I decided to completly refactor the whole process.
In some cases in the docs you may find a holy grail...
So well I read the docs of the communication protocol of the dispenser, I understood it, and I also found an official example code writen in C# (that was so easy to find I don't know why nobody in the development team had tried it), I code the desktop app in C# WPF, so I needed to integrate the dlls of the example code in the code of my app, and design a robust algorithm that handle all the situations in the dispense process.
The point is that I created a console app as a draft with all the functionality that must be in the WPF app, I used an interface provided by the official example, a dll that contains ther principal methods that send the commands to dispenser, initialize, dispense, eject, and get sensors. the console app works perfectly. I thought it would be a piece of cake to implement it in the WPF app, just copy paste the code that i wrote in the console, I was wrong.
When you copy paste your own code and anyway it goes wrong...
When I pasted the code and made all the corrections I run the application and worked fine, it seem that i had done a good job so i started to do several tests in diferent situations, but surprise, sometimes i receive the data stream of the dispenser and sometimes no, causing my application to blow up, I hard debugged the application just to realize that when I put a breakpoint right in the line to send the command to dispenser, the code works perfect again, only in debugging mode, when I delete the breakpoint the WPF app returns to that caothic behaviour.
Be aware of Task management. Thinking about how the lower layers work
First I thought that it would be a problem of execution time, so by using a simple Thread.Sleep();
would solve it, but I was wrong again, it wouldn't be that easy.
By the experience working with WPF I know that strange behaviour between running it with breakpoints and not, is related to Task and Thread management, and yeah, those methods changed to async Tasks 'cause the communication with dispenser is very slow, and I didn't want to freeze the user interface during the process.
I didn't ask this problem to chat GPT 'cause the code and the situation was very specific, I didn't find a way to ask the question appropiately. I was stucked on this problem for like 2 hours, reading the same piece of code over and over again wondering why this was happening to me, so near to solve the problem that no one in the company got the guts to afront.
The "magic" solution
In my personal suffering I had a spark of genius, I was wondering what had changed between the console app, and suddenly it came to me, the only thing that changed was the execution thread, all the code in the console run in a single thread, unlike the WPF app that the backend and UI, in this case, runs in different threads due to avoid the freezing of the UI, the application and dispenser communicate via an open serial port an that occurs inside the dll that i mentioned, I guesed (wondering to be right) that the application had problems synchronizing the multiple changing Task/Threads that exist in the app with the listener of the serial port so it lose the dispenser response, so that's it, I should find a way to run all the dispenser handling code in the same thread.
The "Dispatcher" is your best friend
After figure out the real problem, that was easy, the best thread to execute the dispenser communication is in the main thread and I used the Application Dispatcher in all the dispenser methods.
await Application.Current.Dispatcher.BeginInvoke(() => {
// Your Code
});
So when I test again, the glory, worked perfect, without any exception, I could even run them asynchronously thanks to the BeginInvoke method of the Dispatcher. The best feeling ๐๐.
Final thoughts
I don't have the complete idea of how the Dispatcher deals with the threads and why it affect a serial port communication running from a dll, for example, how the BeginInvoke/Invoke methods can join the main thread? and how it can execute without freezing the UI? (In the case of BeginInvoke), I want to know more about, if anyone reading would like to have a conversation about it or has the answer and would like to explain it to me, you are welcome to do so.
Subscribe to my newsletter
Read articles from Donovan Ramirez Agudelo directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by