Beijer Electronics (formerly QSI Corporation)

Manufacturer of Mobile Data and Human Machine Interface Terminals.
It is currently Sat Nov 18, 2017 6:04 pm

All times are UTC - 7 hours




Post new topic Reply to topic  [ 3 posts ] 
Author Message
PostPosted: Thu Jul 06, 2006 1:50 pm 
Offline
QSI Support
QSI Support
User avatar

Joined: Wed Mar 08, 2006 12:25 pm
Posts: 881
Location: Salt Lake City, Utah
Frequently, I will see code snippets similar to the following:

Code:

dim waitingOnAck as boolean

init waitingOnAck := false



func TransmitSequence()

    Transmit (com1, "Command 1\n", false)



    waitingOnAck := true

    while waitingOnAck do

        'Do nothing, just waiting

    loop



    Transmit (COM1, "Command 2\n", false)

endfunc



func DataReceived (data[] as byte)

    buffer = buffer + data

    if find(buffer, 0, -1, "GotIt\n") then

        buffer = ""

        waitingOnAck := false

    endif

endfunc



or like

Code:

func DoSomeLongThing()

    WaitLabel.enabled = true

    WaitLabel.value = "Staring task..."



    'Do some stuff here



    WaitLabel.value = "Half done"



    'Do some more stuff



    WaitLabel.value = "Complete"

endfunc





On the surface, both of these examples look like good ideas, but in Qlarity they fail horribly. In the first case, the application will lock up when a user calls TransmitSequence. In the second case, the label won't appear to update until after DoSomeLongThing() completes, and will only display the final value ("Complete").



At a high level, this can be understood as Qlarity requires that one event complete before it will process a second one. While programmers may not often think about it, receiving serial data and drawing to the display are both events -- but ones that the Qlarity objects usually handle automatically.



It is important to note that this behavior is by design in order to avoid some of the easy pitfalls afforded by some other languages which allow event reentrancy which leads to frequent programming errors. For a more complete discussion of the low level behavior of Qlarity read the followup post.



The correct solution is to break things up into multiple function calls and build a small state machine, such as this:



Code:

func BeginTransmitSequence()

    if curTxState <> TX_None then

        'Could easily change this to fail silently instead of throwing an exception

        throw ("BeginTransmitSequence", "Already transmitting")

    endif



    Transmit (com1, "Command 1\n", false)

    curTxState = TX_Cmd1

endfunc



func ContinueTransmitSequence()

    if curTxState == TX_Cmd1 then

        curTxState = TX_Cmd2

        Transmit (COM1, "Command 2\n", false)

    elseif curTxState == TX_Cmd2 then

        curTxState = TX_Cmd3

        Transmit (COM1, "Command 3\n", false)

    elseif curTxState == TX_Cmd3 then

        'Done

        curTxState := TX_None

    endif

endfunc



func DataReceived (data[] as byte)

    buffer = buffer + data

    if find(buffer, 0, -1, "GotIt\n") then

        buffer = ""

        ContinueTransmitSequence()

    endif

endfunc





This will not lock up the terminal and allows the terminal to remain responsive, even over the course of a long transmit/receive sequence. The other advantage is that it deals with the problem of reentrancy -- in this example and exception is thrown if you attempt to begin transmitting the sequence while the sequence is already being transmitted.



The solution for updating the display while performing long calculations is similar, but is complicated by the fact that display drawing is the lowest priority task on the terminal. The usual solution is to break up the calculation into small chunks and use a timer to begin processing each chunk. If your application requires something a bit more sophisticated, feel free to post your problem on the forums and we will see if we can help.

_________________
Jeremy
http://www.beijerinc.com


Last edited by Jeremy on Thu Jul 06, 2006 2:41 pm, edited 1 time in total.

Top
 Profile  
 
PostPosted: Thu Jul 06, 2006 2:06 pm 
Offline
QSI Support
QSI Support
User avatar

Joined: Wed Mar 08, 2006 12:25 pm
Posts: 881
Location: Salt Lake City, Utah
The discussion that follows goes into a bit more detail about exactly why the Qlarity engine does not allow reentrancy and exactly what order messages (kind of a low level event) are processed in. The discussion is geared more for those who are at the level of designing their own objects, but should be an interesting read for anyone who is familiar with Qlarity.



After reading this, you may also wish to review chapter 3 of the Programmer's Reference for more details.



In Qlarity all code is executed in response to a message. No code executes unless a message has instigated it. Also in Qlarity, each message executes to completion before the next message is processed. Finally, messages (except MSG_DRAW) are processed in the order they are generated These three concepts are crucial to understanding how Qlarity decides when to process code.



The Qlarity firmware maps low level messages to Qlarity code using the "handles MSG_xxx" syntax which you may have seen in various places. Most of the time, end developers don't need to worry too much about messages. They handle what I call "events" which are functions provided by an object to indicate that something interesting has occurred.



For instance, a user touches the terminal display. The Qlarity firmware takes that screen touch and sees which object(s) were touched. This is translated to a MSG_SCREEN_PRESS and placed in a queue of messages. Deep inside Qlarity, whenever no Qlarity code is executing, a message loop examines the queue and if a message is present removes it and sends it to the appropriate object(s). In this case the MSG_SCREEN_PRESS is removed from the queue and sent to the Button object. The button object examines it and will generate the "HandlePress" event. If "clickonpress" is set to true it will also generate the "Click" event. This is the level at which most code written by application developers is executed. When those functions return, the button object "kills" the MSG_SCREEN_PRESS message by returning true (see Chapter 3 of the Programmer's Reference). This prevents any objects that may be underneath the button from receiving the screen press.



Now, remember what I mentioned in the second paragraph: "Also in Qlarity, each message executes to completion before the next message is processed" This means that if you sit in a loop in one of your event handlers (remember, each event handler is called indirectly in response to a message), then no subsequent messages will be processed. This means that until your loop exits, no screen presses or serial data or drawing or anything else will be processed. If your loop depends on a screen press to set the loop termination variable, then you will lock up the terminal.



Again, it is crucial that your function terminate -- by terminating it releases control back to the firmware to process messages.



Finally drawing: When an object is updated in such a way that it needs to modify its on screen appearance, such as when you change the caption of a label, it calls the Rerender API function. This causes the firmware to generate a MSG_DRAW that will eventually get routed back to the object that requested drawing.



Drawing is always the lowest priority in Qlarity -- this ensures that processing power is dedicated first to any calculation algorightims you have then to updating the display. It also means that if your application enters a state where it is consuming nearly all the processing power, the display will update very slowly. It also has benefit that if you make multiple display changes to an object, it only needs to draw itself once. For instance, changing the caption, the border and the foreground and background colors of a button object takes little more time than simply changing the caption. Under systems which draw immediately this can be a very slow operation as the button would draw four separate times, even though you meant to change all those settings simulteneously.



The one thing to be aware of with drawing as the lowest priority is that it is possible to "starve" the display of updates. For instance the following code:

Code:

const message MSG_DoStuff

func DoStuff (parm as integer) returns boolean

    handles MSG_DoStuff



    UserDirectMsg (default, MSG_DoStuff, 0, false)

    return true

endfunc

would not lock up the terminal -- you could still touch buttons and process serial and ethernet data streams, but the display would never update. This is because this code would always keep at least one message in the Qlarity message queue, and if even one message is in the message queue, drawing will not be processed.

_________________
Jeremy
http://www.beijerinc.com


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 14, 2007 11:28 am 
Offline
User avatar

Joined: Thu Mar 02, 2006 2:12 pm
Posts: 487
Location: Salt Lake City, Utah
You may find that switching from TTF fonts to BDF fonts will help speed up your programs draw times allowing for better responsiveness.

TTF vs. BDF FAQ

_________________
Ron L.

http://www.beijerelectronicsinc.com/


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 3 posts ] 

All times are UTC - 7 hours


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron
Powered by phpBB® Forum Software © phpBB Group