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. |
Re: User Information Display in Running DXL Scripts
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()
Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS
|
Re: User Information Display in Running DXL Scripts
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.
// 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 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.
// 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,
// 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 }
}
Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS
|
Re: User Information Display in Running DXL Scripts
Got busy for a couple of weeks.
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
}
// ************************************************************************************
}
|
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()
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. |