locked
Mobile C# App, Architecture Question (Long) RRS feed

  • Question

  •  

    I am writing a windows mobile app in C# with compact framework 3.5, on VS 2008 SP1, targeted for windows mobile 6 classic:

    Background -

    The Windows mobile app acts as a control / configuration tool for another device, communicating through a serial port on a on the PDA. The software consists of 2 main classes, one which is the main form, and a second which instantiates the serial port class, and wraps some functionality around it for my communications scheme.

    The program sends some serial commands when either A) the users press the buttons on the GUI, or B) my status timer expires, and I request my periodic status packet (approximately once every 2 seconds, but the timing is not critical. )

    My question is as follows...


    What is the best way to coordinate the app, so that if the user presses a button on the GUI (which in turn sends a message), and the status timer expires before the response to the first message is received, the two messages do not conflict? The receiving device is a microcontroller, it cannot process more than one message at a time...

    Some things I considered:

    A) Having a "Message Sent" flag, and then locking the gui code lock in a While "Message Sent" DoEvents loop.

    This turned into a disaster, because if while the GUI was waiting, the user pressed yet another button, the click event gets processed, and the problem was compounded....

    B) Locking the GUI controls every time the user clicks a button or the status timer expires, or the user presses a button, so that multiple messages could just not be sent, because of control lockout.

    This was bad for 2 reasons, One, the GUI was locking and unlocking a lot, which makes it look bad, and 2, even when the GUI was locked, i the status timer expired, I really didnt solve the problem.

    C) Implementing a message queue - where if a second message is sent, the Communications class stores it, and waits for the previously sent message to complete, then sends out the queued message.

    This would solve the problem, but makes my communications class more complex than necessary, so I would like to avoid it if possible .

    D) Just checking the "message Sent" flag, and having the GUI wait in a while loop without a doevents, which locks the GUI.

    Seems to solve the problem, but doesn't seem elegant / correct.


    So - any suggestions welcomed, currently everything is running on one thread, with the exception of the data received event of the comm port being on its own thread because that's how Microsoft wrote the .Net serial port class.

    Thanks if you even made it to the bottom of this message -
    Mike
    Wednesday, October 1, 2008 9:16 PM

Answers

  • It's a simple thing to buttun.Enable = false; inside the button's click event handler to prevent the user from hitting the button until you're ready for them.  Then, when you're ready for them, simply re-enable it agian. 

     

    ***** Be careful when using the DoEvents because it can cause several issues, depending upon when and how you're using it *****

     

    Your third item ... "C" .... can also be implemented quickly.  Simply place all your outgoing "messages" into some type of Queue, List, Dictionary, Array and have your communication code running on a background thread.  It would grab the various messages that the user "deposited" into the collection and handle them one at a time.  If you need to inform the user of anything then simply invoke back over and post your update (control.BeginInvoke or control.Invoke).  All your GUI is doing in this scenario is depositing items into the list, and posting the results that your communication logic may care to share.  But the communication is handled in the background which allows your GUI to worry about other things (I always take ANYTHING that I have no idea on "how long is it going to take?" and run that logic on a different thread).

     

    "D" .... having your app spin in a loop will run down the battery quicker then it need be.  Try using a ManualResetEvent to control the iteration as a possible alternative.

    Wednesday, October 1, 2008 10:36 PM
  •  Mikey0727 wrote:

    Why so cautious with doevents?  Aside fromn the fact that realizing while I'm waiting, there may be another button click event fired, which could end up building up a bit of a call stack -

    Everything when applied with discipline has its place.  But the behaviour and ramifications of Application.DoEvents is often misunderstood by developers and can lead to some rather interesting problems.  In the Windows Mobile world misuse of the method can lead to a program killing the battery in a device. It is a better idea to avoid using it until you have a full understanding of what can go wrong from calling it.  There's plenty of post on the web about it.  Just do a search fot Application.DoEvents and Evil.

     

    Is DoEvents Evil, Revisited

     

    Thread.Sleep(0) and Application.DoEvents

    Thursday, October 2, 2008 1:49 PM

All replies

  • It's a simple thing to buttun.Enable = false; inside the button's click event handler to prevent the user from hitting the button until you're ready for them.  Then, when you're ready for them, simply re-enable it agian. 

     

    ***** Be careful when using the DoEvents because it can cause several issues, depending upon when and how you're using it *****

     

    Your third item ... "C" .... can also be implemented quickly.  Simply place all your outgoing "messages" into some type of Queue, List, Dictionary, Array and have your communication code running on a background thread.  It would grab the various messages that the user "deposited" into the collection and handle them one at a time.  If you need to inform the user of anything then simply invoke back over and post your update (control.BeginInvoke or control.Invoke).  All your GUI is doing in this scenario is depositing items into the list, and posting the results that your communication logic may care to share.  But the communication is handled in the background which allows your GUI to worry about other things (I always take ANYTHING that I have no idea on "how long is it going to take?" and run that logic on a different thread).

     

    "D" .... having your app spin in a loop will run down the battery quicker then it need be.  Try using a ManualResetEvent to control the iteration as a possible alternative.

    Wednesday, October 1, 2008 10:36 PM
  •  

    The coordination of allowing the threads to send messages through your serial port is a classic synchronization problem with a simple solution.  If you are using C# then use the lock keyword to ensure that only one thread is sending a message at a time.  If you use VB.Net then you will need to use Monitor.Enter and Monitor.Exit instead (They are the exact same thing, just different notation).   A full discussion on multithreaded programming could get to be a bit lengthy but look up the lock keyword or the Monitor.Enter/Monitor.Exit methods.

     

    Having no familiarity with your application it is difficult to make assertions about  how the UI would work and whether or not it makes sense to queue multiple user messages.  For example, I don't know the consequence of a user making a mistake when they queue the messages and whether or not the user should have any notion of an undo for removing mistakes from your message queue.

     

    Let us for now assume you decided to go with a non-queued interface.  Instead of locking the controls you could also display a visual indicator that user command is being sent, preferable something that changes color and is recognizable without looking directly at it. If the user tries to send a message while the indicator is on have the device respond by beeping and flashing the indicator on the screen.

     

    If you decided to use queueing then you may want to give a visual indicator of what commands are enqueue and remove the items from display as they are sent over the serial port.  If you queue has a maximum capacity that you think will be reached once again have an indicator that changes color and make the interface flash and beep if the user tries to exceed the limit.

    Wednesday, October 1, 2008 11:52 PM
  •  

    Thanks for your suggestions - I think i will implement a message list / queue, and i am going to research the "Lock" keyword as another poster suggested. 

     

    At this point in my App, the Comm code doesnt run on a seperate thread (only the data received event, but thats a function of the serial port class).  I didnt put it on another thread intentionally because then I'd have to deal with 3 threads, but maybe that is the right choice?  Then I could have Comm thread constantly checking the message queue -

     

    Why so cautious with doevents?  Aside fromn the fact that realizing while I'm waiting, there may be another button click event fired, which could end up building up a bit of a call stack -

     

    The button.enable = false wont work for me, several panels with buttons doing all kinds of things with different messages assigned to them, so for that to work, I'd have to lock lost of GUI every time I sent a mesage. 

     

    The lock Keyword sounds like a good solution as well - my main problem is that user action is not predictable, and if my status request message happened to expire exactly when a user also sent his own contrrol messages, I would have a conflict...  If I use a threading timer, so the timer is on its own thread - sounds like I could keep this from happening -

     

     

    Thursday, October 2, 2008 1:12 PM
  •  Mikey0727 wrote:

    Why so cautious with doevents?  Aside fromn the fact that realizing while I'm waiting, there may be another button click event fired, which could end up building up a bit of a call stack -

    Everything when applied with discipline has its place.  But the behaviour and ramifications of Application.DoEvents is often misunderstood by developers and can lead to some rather interesting problems.  In the Windows Mobile world misuse of the method can lead to a program killing the battery in a device. It is a better idea to avoid using it until you have a full understanding of what can go wrong from calling it.  There's plenty of post on the web about it.  Just do a search fot Application.DoEvents and Evil.

     

    Is DoEvents Evil, Revisited

     

    Thread.Sleep(0) and Application.DoEvents

    Thursday, October 2, 2008 1:49 PM
  • I checked out the articles, and the re-entrancy problem makes sense:

     

    Currently in My app i have a few things I need to do that are not one serial command, but a command sequence, that need to be executed in order.  Moreover, during that sequence, I don't want the user to do anything,

     

    So I pop up a small dialog form to show a status message and have the following -

     

    if (_Result_OK)

    {

    POPUP_DIALOG("Status Message One");

    Command1();

     

    //---Wait

    while (_Result_Pending)

    {

    Application.DoEvents();

    }

    }

     

    if (_Result_OK)

    {

    POPUP_DIALOG("Status Message Two");

    Command2();

     

    //---Wait

    while (_Result_Pending)

    {

    Application.DoEvents();

    }

    }

     

    The Command Functions set _Result_Pending to true, and the  DatareceivedEvent Sets _Result_Pending = false , when a response is recieved.  I put the DoEvents in there to ensure the serial port data received event would get processed, but maybe this architecture is best replaced my one that makes use of a Communications thread and a ManualResetEvent? 

     

     

    Thursday, October 2, 2008 6:08 PM
  • Good conclusion.

     

    Doesn't have to be implemented that way, but the reset event will allow you to control how a specific thread progresses, and by placing your comm code in a different thread it doesn't require you to call DoEvent as a way of maintaining responsiveness (which as you're starting to see can have some issues of its own).

     

    Not wanting to put too much on your plate, check into the APM (Asychronous Programming Model) and the general idea of threading, thread pools, delegate callbacks, and delegate events.  What you'll find there is a way to keep all your long running tasks in their own threads that fire "update" events back to anyone (or, any component) who cares.  Also, the idea that you don't have to use a blocking call - like a reset event - to control how a thread marches along through the code .... kind of a more event driven, encapsulated, mutithreaded approach.

     

    Food for thought ...

     

    Thursday, October 2, 2008 6:13 PM
  • Definitely Makes sense for next time, as a lesson learned.   However, being I am kind of far down a path (albeit, not the best one), what do you think of using ManualResetEvent to fix this - Consider the Code below instead of what I had before:

    if (_Result_OK)

    {

    POPUP_DIALOG("Status Message One");

    Command1();

    ManualResetEvent.Reset();      //Reset The Handle -

    ManualResetEvent.WaitOne()  //Make GUI thread wait

    }

     

    if (_Result_OK)

    {

    POPUP_DIALOG("Status Message Two");

    Command2();

    ManualResetEvent.Reset();      //Reset The Handle -

    ManualResetEvent.WaitOne()  //Make GUI thread wait

    }


    Inside the Datareceived Event of the serialport I would call ManualResetEvent.set() - to allow the code above to continue, after i got the response. (ManualResetEvent would be an allocated Manualresetevent object, I just wrote it this way for simplicity)

    This approach has obvious pitfalls, in that the thread locks itself, if the datareceived event never fires, I deadlocked, but my comm code has a threading timer that occurs on command timeout, in which I can set the manualresetEvent, and so this problem would be soved.

    If my understanding is correct, if a WaitOne() is called while the ManualResetEvent is Set, the code will just continue on?  (Sorry, never really needed all this threading before, I have been doing a lot of reading today LOL)

    I realize this will block the GUI thread which is terrible practice, but practically for this particular APP, when I am in this long command sequence, the User isnt doing anything but waiting to see status messages anyway... 

    What you think?  While obviously not the best, it is an improvement over what i had earlier, allows me to solve my sychronization problem, and is better than re-architecting almost everything I've done - 
    Thursday, October 2, 2008 7:30 PM