User Information Display in Running DXL Scripts

I write a lot of System Architect VBA and DOORS dxl scripts. I have developed a standard form for SA that displays information to the user to let them known the program is running, doing something, and making progress. I would like to do the same for DXL. I have created several Dialog boxes that include canvases, so I have some experience with that. Keep in mind that I don't need the user to interact with the program, such as by throwing up a modal information or acknowledge box. I just want a Dialog box that updates so the user has a warm and fuzzy feeling the program is active, and he doesn't need to mess with it. Both SA VBA and DOORS dxl run so fast they do not let the OS have time to update the screen and check for mouse clicks. VBA gets around this with a command called DoEvents, which specifically releases the running code to the OS for a brief period of time. DOORS dxl does not appear to have anything like this.
Which brings us to the Progress Bar. This seems to be the ONLY element in DOORS that allows for screen updates within a running program. If it could be used as an element in a Dialog box, this might be acceptable. But it creates its own box which often gets lost on a screen.
So the question is this. Is there some way of getting DOORS to update a Dialog box from a running DXL script without using the progress Bar? I have tried all kinds of things using Block and Show, even calling the repaint routines directly. And while this works, (The routines get called because status messages written to a file show up) the OS never updates the screen until the routine stops, at which point I get a fury of updates all at once. I have also noticed that while the buttons move when clicked, the callback routine never receives the click from the OS, so the action the button was supposed to do never happens (I normally have a Stop command as one of the buttons) Again, I can implement this with a Progress Bar, but I would rather be able to use all the Dialog Box features.
JeffreyR - Thu Jun 28 15:12:19 EDT 2012

Re: User Information Display in Running DXL Scripts
Mathias Mamsch - Fri Jun 29 18:29:46 EDT 2012

The only way I have achieved this warm and fuzzy feeling is to replace the main loop of the program by a timer. The idea is here, that when you click the action button instead of doing all the work in the callback you activate a timer. Then in the timer callback you do one item after another, keeping progress in a global variable.
 

pragma runLim, 0
 
// we use this for tracking progress
int giCurrentProgress = 0
int giMaxProgress     = 0
DB diag
DBE theTimer 
DBE sbStatus
 
void TimerCallback (DBE x) {
    int tStart = getTickCount_()  
    // keep working for 75 ms, letting dxl IDLE 25 ms
    while (getTickCount_ - tStart < 75) {
        // do your work 
        set (sbStatus, "Current: " giCurrentProgress "\n")
      giCurrentProgress++
        sleep_ 10
    }
    if (giCurrentProgress > giMaxProgress) { stopTimer theTimer }
}
 
void startWork (DBE x) {
    giMaxProgress = 10000
    startTimer theTimer 
}
 
void pauseWork (DBE x) { stopTimer theTimer }
void closeFunc (DB x) { release x; hide x }
 
void makeGUI () {
    diag = centered "Hello" 
    button (diag, "Start", startWork) 
    button (diag, "Pause", pauseWork) 
    close (diag, false, closeFunc)
    int sbSections[] = {2000}
    sbStatus = statusBar (diag, "", sbSections)
    // Set timer to 100 ms 
    theTimer = timer(diag, 0.1, TimerCallback, "ticktock")
    stopTimer theTimer 
    realize diag
    setSize(diag, 150, 150)
    show diag
}
 
makeGUI()

 


To have optimal speed you can have the timerCallback work for a certain amount of time, in the example for 75 ms when the timer is set to 100ms giving doors a 25% idle time. Of course this approach breaks down, as soon as you have operations that take considerable more time than 75ms like opening a module, but you can use this approach for doing comparisons, scanning modules, etc.

Maybe that helps, Regards, Mathias

 

 

 


Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS

 

 

Re: User Information Display in Running DXL Scripts
JeffreyR - Mon Jul 02 14:44:57 EDT 2012

Well, that got some of it. Using what you provided and playing around a lot I now have a Display that updates. With the sleep command in there, it updates rapidly then stops for a couple of seconds when I think DOORS is writing the output to the file. Without the sleep command it updates about once a second, again while doing the output. This is acceptable. But what does not happen is the buttons being available. I understand what you did in your code. Essentially, the timer function acts like a button that is automatically pushed at a certain time interval. You check the internal ticker and exit the callback after 75% of the time. At this point it is the same as if there was a Dialog box sitting there waiting for user input. So the buttons work under your arrangement. But when you start looping through modules, which is used in most DOORS code, this won't work because the Timer callback would pull you out of the loop. There might be a mess of code that could be put in to get you back into the loop in the right place. But if you compound the problem with a second loop internal to the first, such as when you are checking links, the code gets mighty convoluted. But at least this is a start. Note in the code the way it is written the DontStop variable is never set false in the StopWork routine because DOORS never realizes the button was pushed. If there were two timers available, I could set a flag with the first that would call a routine that would set the second timer for a short interval, then wait a bit, just like the code you provided. But it does not appear you can have more than one timer. I wonder, however, why the sleep command does not allow for receiving mouse clicks. The operating system does buffer mouse movements, and I have seen DOORS do a whole mess of things once the dxl stops running. One would think the sleep command would allow the capture and eventual processing of mouse clicks. But apparently not.

The code below is a typical usage of looping through a module. It works as it sits, and updates as stated above. If you have some way of adding button responses to this I would become a really happy person.

// DOORS Update Label Test
// June 9, 2012 - Jeff Roberts
// Copyright (c) 2012 Advanced Sciences and Technology. All Rights Reserved.
 
/*   This tool Tests the Sleep command as a way to update a display
    
     2012-07-01 - 0.50 - JMR - Created
     ************************************************************************************
*/
     string GlobalDir = "\\\\Client\\C$\\Working\\"             // Global User Directory
     string ToolDir = "DOORS Files\\"                           // Tools Directory
    
     Stream output                               // Output File Stream
 
     string TargetMod = "/OPC HM_E and C4ISR July 2011/L1-USCG Missions and Capability/"
     TargetMod = TargetMod "L2-Operational Requirements Documents/L3 - OPC/"
     TargetMod = TargetMod "OPC Surface ORD"
     Module TMod = null                          // ORD Module Reference
     Object o = null
     string t
     DB MainForm                                 // Main Display Form
     DBE DisLabel                                // Display Label
     bool DontStop = true
 
void StopWork(DBE x) {
     DontStop = false
}
     
void startWork(DBE x) {
     TMod = read(TargetMod, true)
     for o in TMod do {
        if(!DontStop) continue
        t = "Working On " identifier o ""
        set(DisLabel, t)
        realize MainForm
        sleep_(2)                               // Sleep for 2 milliseconds
        output << DontStop "\t"
        output << identifier o "\t"
        output << o."Object Text" "\n"
     }
     flush output
     close output
     close TMod
     hide MainForm
     destroy MainForm
     MainForm = null
     print "We are Done"
     halt
} 
     
pragma runLim, 0
 
     MainForm = create ("Test Update",  styleFixed | styleCentered)
     DisLabel = label(MainForm, "This Should Update")
     button (MainForm, "Start", startWork)
     button (MainForm, "Stop", StopWork) 
     
     t = GlobalDir ToolDir "Test Update Display.txt"
     output = write(t)
     realize MainForm
     setSize(MainForm, 250, 150)     
     show MainForm

Re: User Information Display in Running DXL Scripts
Mathias Mamsch - Sun Jul 08 09:38:27 EDT 2012

JeffreyR - Mon Jul 02 14:44:57 EDT 2012

Well, that got some of it. Using what you provided and playing around a lot I now have a Display that updates. With the sleep command in there, it updates rapidly then stops for a couple of seconds when I think DOORS is writing the output to the file. Without the sleep command it updates about once a second, again while doing the output. This is acceptable. But what does not happen is the buttons being available. I understand what you did in your code. Essentially, the timer function acts like a button that is automatically pushed at a certain time interval. You check the internal ticker and exit the callback after 75% of the time. At this point it is the same as if there was a Dialog box sitting there waiting for user input. So the buttons work under your arrangement. But when you start looping through modules, which is used in most DOORS code, this won't work because the Timer callback would pull you out of the loop. There might be a mess of code that could be put in to get you back into the loop in the right place. But if you compound the problem with a second loop internal to the first, such as when you are checking links, the code gets mighty convoluted. But at least this is a start. Note in the code the way it is written the DontStop variable is never set false in the StopWork routine because DOORS never realizes the button was pushed. If there were two timers available, I could set a flag with the first that would call a routine that would set the second timer for a short interval, then wait a bit, just like the code you provided. But it does not appear you can have more than one timer. I wonder, however, why the sleep command does not allow for receiving mouse clicks. The operating system does buffer mouse movements, and I have seen DOORS do a whole mess of things once the dxl stops running. One would think the sleep command would allow the capture and eventual processing of mouse clicks. But apparently not.

The code below is a typical usage of looping through a module. It works as it sits, and updates as stated above. If you have some way of adding button responses to this I would become a really happy person.

// DOORS Update Label Test
// June 9, 2012 - Jeff Roberts
// Copyright (c) 2012 Advanced Sciences and Technology. All Rights Reserved.
 
/*   This tool Tests the Sleep command as a way to update a display
    
     2012-07-01 - 0.50 - JMR - Created
     ************************************************************************************
*/
     string GlobalDir = "\\\\Client\\C$\\Working\\"             // Global User Directory
     string ToolDir = "DOORS Files\\"                           // Tools Directory
    
     Stream output                               // Output File Stream
 
     string TargetMod = "/OPC HM_E and C4ISR July 2011/L1-USCG Missions and Capability/"
     TargetMod = TargetMod "L2-Operational Requirements Documents/L3 - OPC/"
     TargetMod = TargetMod "OPC Surface ORD"
     Module TMod = null                          // ORD Module Reference
     Object o = null
     string t
     DB MainForm                                 // Main Display Form
     DBE DisLabel                                // Display Label
     bool DontStop = true
 
void StopWork(DBE x) {
     DontStop = false
}
     
void startWork(DBE x) {
     TMod = read(TargetMod, true)
     for o in TMod do {
        if(!DontStop) continue
        t = "Working On " identifier o ""
        set(DisLabel, t)
        realize MainForm
        sleep_(2)                               // Sleep for 2 milliseconds
        output << DontStop "\t"
        output << identifier o "\t"
        output << o."Object Text" "\n"
     }
     flush output
     close output
     close TMod
     hide MainForm
     destroy MainForm
     MainForm = null
     print "We are Done"
     halt
} 
     
pragma runLim, 0
 
     MainForm = create ("Test Update",  styleFixed | styleCentered)
     DisLabel = label(MainForm, "This Should Update")
     button (MainForm, "Start", startWork)
     button (MainForm, "Stop", StopWork) 
     
     t = GlobalDir ToolDir "Test Update Display.txt"
     output = write(t)
     realize MainForm
     setSize(MainForm, 250, 150)     
     show MainForm

Hey Jeffrey,

the buttons will work in that solution. If you want to loop through modules you would need to get rid of the loop, because this will send DOORS blocking until the loop is finished.
 

// Fill a skip with all objects to progress 
Skip skObject = create() 
{
   int count = 0
   for o in entire mod do { put (skObjects, count++, o) }
}
giMaxProgress = count
 
void TimerCallback (DBE x) {
    int tStart = getTickCount_()  
    // keep working for 75 ms, letting dxl IDLE 25 ms
    while (getTickCount_ - tStart < 75) {
        // do your work 
        set (sbStatus, "Current: " giCurrentProgress "\n")
        Object o = null 
 
        // get the object where we left off 
        find (skObject, giCurrentProgress, o) 
        if (!null o) {
          // do all your work with that object here ... The timer callback will be 
          // processing objects for 75 ms, then go back to the main loop, then take on where it left off
        }
        giCurrentProgress ++
        
    }
    if (giCurrentProgress > giMaxProgress) { stopTimer theTimer }
}

 


Maybe that helps, regards, Mathias

 

 


Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS

 

Re: User Information Display in Running DXL Scripts
JeffreyR - Tue Aug 07 09:18:34 EDT 2012

Got busy for a couple of weeks.
I took the in the posts above advice and modified it to work in my environment. The code below shows the routine I came up with. Keep in mind that I don't need an immediate response, just a response in a reasonable amount of time (say 5 seconds) that will allow control over a runaway program. The hard part was getting everything in the right order so DOORS will compile it. But I now have a display that updates properly and buttons that work. The ChangeDisplay variable was put in there because otherwise the display would blink every second or so. The IsRunning variable is a control, and is set true unless the program gets terminated through an error or user intervention.
 

void TimerCallback (DBE Timex) {
/*   This is the Timer Routine. The routine will check the value of the boolean
     WaitTime. If True, the routine will set it false, stop the timer, and call the
     MasterControl routine. It is the responsibility of the MasterControl routine
     to know where it is in the process and act accordingly. The last thing the
     MasterControl does on each call is restart the timer. If WaitTime is false,
     this routine will set it true, repaint the display, and start the timer. Then
     it will exit. This essentially puts the DXL script into idle mode for about
     100 milliseconds, which appears to be the minimum value for the timer function.

     2012-08-03 - 0.50 - JMR - Created
     ************************************************************************************
*/
     if(WaitTime && IsRunning) {                 // In Wait Mode
        WaitTime = false                         // Set to Run Mode
        stopTimer TheTimer                       // Let Work Happen
        MasterControl                            // Do Actual Work
     } else {                                    // In Run Mode
        WaitTime = true                          // Put into Wait Mode
        if(ChangeDisplay) {                      // Display has been Updated
           RepaintMainForm(DisplayCanvas)        // Repaint the Screen
        }
        startTimer TheTimer                      // Wait .1 Seconds
     }
//   ************************************************************************************
}


I consider this question as answered now.

Re: User Information Display in Running DXL Scripts
BeastBoysDad - Fri Oct 04 19:27:28 EDT 2013

Mathias Mamsch - Fri Jun 29 18:29:46 EDT 2012

The only way I have achieved this warm and fuzzy feeling is to replace the main loop of the program by a timer. The idea is here, that when you click the action button instead of doing all the work in the callback you activate a timer. Then in the timer callback you do one item after another, keeping progress in a global variable.
 

pragma runLim, 0
 
// we use this for tracking progress
int giCurrentProgress = 0
int giMaxProgress     = 0
DB diag
DBE theTimer 
DBE sbStatus
 
void TimerCallback (DBE x) {
    int tStart = getTickCount_()  
    // keep working for 75 ms, letting dxl IDLE 25 ms
    while (getTickCount_ - tStart < 75) {
        // do your work 
        set (sbStatus, "Current: " giCurrentProgress "\n")
      giCurrentProgress++
        sleep_ 10
    }
    if (giCurrentProgress > giMaxProgress) { stopTimer theTimer }
}
 
void startWork (DBE x) {
    giMaxProgress = 10000
    startTimer theTimer 
}
 
void pauseWork (DBE x) { stopTimer theTimer }
void closeFunc (DB x) { release x; hide x }
 
void makeGUI () {
    diag = centered "Hello" 
    button (diag, "Start", startWork) 
    button (diag, "Pause", pauseWork) 
    close (diag, false, closeFunc)
    int sbSections[] = {2000}
    sbStatus = statusBar (diag, "", sbSections)
    // Set timer to 100 ms 
    theTimer = timer(diag, 0.1, TimerCallback, "ticktock")
    stopTimer theTimer 
    realize diag
    setSize(diag, 150, 150)
    show diag
}
 
makeGUI()

 


To have optimal speed you can have the timerCallback work for a certain amount of time, in the example for 75 ms when the timer is set to 100ms giving doors a 25% idle time. Of course this approach breaks down, as soon as you have operations that take considerable more time than 75ms like opening a module, but you can use this approach for doing comparisons, scanning modules, etc.

Maybe that helps, Regards, Mathias

 

 

 


Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS

 

 

 

The timer function referenced above is not documented in the DOORS DXL Reference Manual.  I've asked IBM to provide documentation and they declined.  I've submitted an RFE to ask IBM to add the timer function to the DXL Reference Manual.  Please vote on the proposed RFE at http://www.ibm.com/developerworks/rfe/execute?use_case=viewRfe&CR_ID=39914.