Using REPEAT a block till an event occurs? - progress-4gl

This is my issue:
On triger of a START button i wan to execute one block. And it should stop executing when I
press STOP button.
Eg
on triger of start button:
REPEAT:
message "hai".
END.
and when I press STOP button It should stop. What additional condition should I give to REPEAT block?Preferably I dont want to write the condition in STOP button Triger..Please give suggestion .

Progress is not multi-threaded.
So you have to carefully think through which bits of code need to execute in what context in order to fake it. (More carefully than my initial "it cannot be done" response.)
As "firhang" points out "PROCESS EVENTS" can be used to check for events queued and ready to be acted on.
So you could run your loop inside an event handler and have that event handler listen for additional events.
I believe that the following satisfies the original question and works in both GUI and Character clients:
define variable i as integer no-undo.
define variable bStop as logical no-undo.
define button butStart label "Start".
define button butStop label "Stop".
form butStart butStop with frame but-frame row 1.
on choose of butStop bStop = true.
on choose of butStart do:
bStop = false.
do while bStop = false:
i = i + 1.
process events.
message i.
end.
message "Paused".
end.
enable butStart butStop with frame but-frame.
pause 0 before-hide.
wait-for window-close of current-window.

PROCESS EVENTS statement
Processes all outstanding events without blocking for user input.
Syntax
PROCESS EVENTS
Example
This procedure counts to 1,000 until you choose STOP:
r-proevs.p
DEFINE VARIABLE ix AS INTEGER NO-UNDO.
DEFINE VARIABLE stop-sel AS LOGICAL NO-UNDO.
DEFINE BUTTON stop-it LABEL "STOP".
DISPLAY stop-it.
ON CHOOSE OF stop-it
stop-sel = TRUE.
ENABLE stop-it.
DO ix = 1 TO 1000:
DISPLAY ix VIEW-AS TEXT.
PROCESS EVENTS.
IF stop-sel THEN LEAVE.
END.
On each pass through the loop, the procedure displays the new value of ix and then checks whether any events are waiting to be processed. If no events have occurred, execution continues and the loop iterates. If the STOP button has been chosen, that event is processed changing the value of stop-sel. When execution continues, the program exits the loop.
If the loop does not contain the PROCESS EVENTS statement, the choose event never processes and the loop iterates until ix equals 1,000.
Notes
The WAIT-FOR statement processes all pending events and blocks all other execution until a specified event occurs. The PROCESS EVENTS statement processes all pending events and immediately continues execution with the next statement.
If there are any asynchronous requests for which PROCEDURE-COMPLETE events have been received but not yet processed, this statement processes these events as described for the WAIT-FOR statement.
You cannot call the .NET method system.Windows.Forms.Application:DoEvent( ) in ABL. The PROCESS EVENTS statement performs the function of this method.
.NET can raise exceptions in the context of an ABL session when this statement executes.

DEFINE VARIABLE bStop AS LOGICAL NO-UNDO.
DEFINE BUTTON butStop LABEL "Stop".
FORM butStop WITH FRAME but-frame ROW 1.
ON CHOOSE OF butStop bStop = TRUE.
ENABLE butStop WITH FRAME but-frame.
REPEAT:
PROCESS EVENTS.
MESSAGE "hai".
IF bStop THEN LEAVE.
END.
MESSAGE "stoped".
WAIT-FOR WINDOW-CLOSE OF CURRENT-WINDOW.

Related

How can I queue code to be run without interrupting currently-executing code?

I'm writing a function that does some work to provide a result to the caller; but once this work is done there exists some followup work that is not required by the caller but is known is going to be useful in the future. I'd like this followup work to be executed when the MATLAB process is no longer busy doing what is immediately required of it so it doesn't slow down, or worse block execution of, other priority tasks.
On the front end interface, MATLAB has a clear sense of whether code is currently executing. When there is, the status bar reads "Busy" and the Editor ribbon strip updates to show a "Pause" button to interrupt execution. You can enter new commands to the command window, but they aren't read and executed until the currently executing code completes. Once execution is completed, the status bar is cleared of text, the ribbon "Pause" button is replaced with a "Run" button, and the command window displays a >> indicating that new commands entered there will be executed immediately.
Effectively I'd like code running in the middle of a function to be able to have the equivalent effect of entering new commands into the command window, to be run after currently-executing code is done.
For example, consider the function:
function testfun(x)
disp("Starting main execution with input " + x);
pause(1)
disp("Completed main execution with input " + x);
disp("Starting followup execution with input " + x);
pause(1)
disp("Completed followup execution with input " + x);
end
A function or script that invokes this a couple of times (and where the extra disp calls are, there could be other unspecified time-consuming activities):
testfun(1)
disp("Done testfun(1).")
testfun(2)
disp("Done testfun(2).")
would result in the output:
Starting main execution with input 1
Completed main execution with input 1
Starting followup execution with input 1
Completed followup execution with input 1
Done testfun(1).
Starting main execution with input 2
Completed main execution with input 2
Starting followup execution with input 2
Completed followup execution with input 2
Done testfun(2).
and take four seconds in total. An alternative solution would result in the output:
Starting main execution with input 1
Completed main execution with input 1
Done testfun(1).
Starting main execution with input 2
Completed main execution with input 2
Done testfun(2).
Starting followup execution with input 1
Completed followup execution with input 1
Starting followup execution with input 2
Completed followup execution with input 2
The output Done testfun(2), which is the last of the higher-priority output results to appear, would only have taken 2 seconds to appear, rather than the original 4.
Is this possible? Alternatively, if I could allow execution to clear the current execution stack (as I could if interrupting the code with the debugger, then using dbstep out all the way up to the Base workspace, then calling new code from the command window) this would be a useful compromise even if it leaves open the possibility there are more function calls queued in the Base workspace. (The actual choice of workspace the code executes in doesn't matter too much, but stepping out of deeper nested workspaces would at least allow the remaining code queued within those workspaces to be completed before the workspace is destroyed.)
The best part-solution to this I'm aware of, with thanks to Jan Simon at MathWorks, is to observe the actual status bar of the MATLAB window. This is accessible with:
statusbar = com.mathworks.mde.desk.MLDesktop.getInstance.getMainFrame.getStatusBar;
The text contained in the status bar is then retrievable with statusbar.getText. When code is executing, the text contains the word "Busy", and when nothing is executing it doesn't, so I can use logic from this to decide MATLAB is currently busy (with careful attention to the possibility that other things, such as the profiler, will also modify the contents of this text).
A timer can poll this text so a callback will fire a short time (though not instantly, and dependent on how aggressively I'm willing to poll the interface) after MATLAB's Busy status ends.
This is not ideal because it only indirectly infers the busy status from a UI element that isn't designed to be involved in application control, and may be unreliable, and because it depends on repeated polling, but in some circumstances it will be better than nothing.
Applying this to the simplified example, we can break the task into two functions, with the main one taking an extra argument t to receive a timer object to interact with:
function testmain(x,t)
disp("Starting main execution with input " + x);
pause(1)
disp("Completed main execution with input " + x);
t.UserData.followups(end+1) = x;
end
function testfollowup(x)
disp("Starting followup execution with input " + x);
pause(1)
disp("Completed followup execution with input " + x);
end
A callback for the timer could then be:
function followupcallback(t,~)
if isempty(t.UserData.followups)
return
end
status = t.UserData.statusbar.getText;
if ~isempty(status) && contains(string(status),"Busy")
return
end
arrayfun(#testfollowup,t.UserData.followups);
t.UserData.followups = [];
t.stop
end
and the polling timer can then be set up:
t = timer( ...
"TimerFcn",#followupcallback, ...
"Period",0.25, ...
"ExecutionMode","fixedRate", ...
"UserData",struct( ...
"statusbar",com.mathworks.mde.desk.MLDesktop.getInstance.getMainFrame.getStatusBar, ...
"followups",[]));
t.start;
With the example script modified to pass the timer through:
testfun(1,t)
disp("Done testfun(1).")
testfun(2,t)
disp("Done testfun(2).")
The resulting output is reorded as intended.
I think you could build an actual work queue. I'm using a global variable here for that, so that multiple different functions can add to the work queue. I'm also using nested functions to encapsulate arbitrary code and variables into a function handle that can be stored and run later on.
I'm still hazy as to why this would be useful, but it does process things in the order given by the example in the OP.
initialize_work_queue
testfun(1)
disp("Done testfun(1).")
testfun(2)
disp("Done testfun(2).")
process_work_queue
disp("Done process_work_queue.")
function testfun(x)
disp("Starting main execution with input " + x);
pause(1)
disp("Completed main execution with input " + x);
function follow_up_work
disp("Starting followup execution with input " + x);
pause(1)
disp("Completed followup execution with input " + x);
end
global my_work_queue
my_work_queue{end+1} = #follow_up_work;
end
function initialize_work_queue
global my_work_queue
my_work_queue = {};
end
function process_work_queue
global my_work_queue
while ~isempty(my_work_queue)
func = my_work_queue{1};
my_work_queue(1) = [];
func();
end
end

Why do not agents go out from the queue?

I have a queue,followed by selectoutput. the problem is that the agent does not go out from the queue.In this case the agent should choose the third exit of the selectoutput, so it is not possible (i presume) that the agent does not go out because the following block is full.What could be the reason? Thanks
the reason is that the condition that you think is true, is actually false, because anylogic calculates first the condition in the select output block, and then it runs the code on the "on exit" action of the queue7 block...
So my guess here, is that you are doing something on the on exit action, that changes the result for the condition.. but it's already too late..
use the "on at exit" action or sometimes you need to put a dummy delay of 1 milisecond or so between your last block before the selectoutput and the selectoutput to ensure your condition is met correctly
This is the order in which things happen:
on at exit queue
check condition
on exit queue
on enter select output

Is it possible to dynamically update if functions in anylogic?

I'm using a state chart in combination with a schedule in Anylogic (see picture). The output of the schedule is equal to 1 during working hours and 0 otherwise. Now I want the transition from state1 to state2 to happen when the schedule is turning to 1 or is equal to 1(so otherwise wait until the working hours).
I have tried using an if statement
if( main.plannerSchedule()==1 )(<perform action>)
However, by this method, the state transition only happens if the statement is true but doesn't wait for it to become true. Is there a way to constantly update the state transition or is there a "wait until" function that can solve this problem?
Best let the schedule send a message to your statechart when it switches. This assumes that the statechart lives on the same agent type as the schedule. Write this code in the action code box of the schedule:
if (value==1) {
statechart.fireEvent("go to state 2");
}
Obviously, your message transition needs to await the "go to state 2" message.
Note the value keyword. See https://www.benjamin-schumann.com/blog/2016/2/4/the-magic-lightbulb-and-how-it-can-help-your-anylogic-modelling for more info on that

Override 'Cancel' in event procedures

There is data validation in my MS Word user form which returns the focus to the textbox where the user entered something incorrect. Now I am trying to accommodate the user's change of mind: instead of correcting the entry, I want him to be able to exit the form (click the Exit command button), in which case the entry would be discarded. I suppose that a solution would start with not using the text box's exit event. I little help from someone who knows the answer would save me a lot of testing time, perhaps to find out that I can't do it.
Does anyone know?
I understand that you are handling the Exit event of the Textbox, setting the Cancel output parameter if the data is not valid.
There's a tricky but simple solution that permits to keep that working and still have an Exit button. It permits to activate the handler of the Exit button without requiring the focus to leave the Textbox. This way you can unload the Form safely in this handler.
Try this it works pretty smoothly:
1- Set the property TakeFocusOnClick of the Exit command button to False. You can do that at design time in the property-sheet, or at run-time i.e. at UserForm_Activate
2- just unload the form when the Exit button is clicked:
Private Sub ExitButton_Click()
Unload Me
End Sub
#A.S.H provided the key to the solution below. His point is that it is possible to call another event procedure while Cancel is active in the Exit procedure of a control. That other procedure can be used to rectify the condition in the first control which is triggering the Cancel, thereby enabling an orderly exit. The all-enabling condition is that the control on whose click event the rectifying procedure is to run must not take the focus when clicked (meaning it can run without triggering an exit from the control stuck on Cancel). I have added code to the exit procedure to set CmdExit.TakeFocusOnClick = False when a Cancel condition arises there. Now, ...
Private Sub CmdExit_Click()
' 12 May 2017
' if CmdExit can't take the focus it can't be the ActiveControl
If Not ActiveControl Is CmdExit Then
Select Case ActiveControl.Name
Case "Cbx107"
Cbx107.Value = ""
Case "Tbx53"
Tbx53.Value = "0"
End Select
With CmdExit
If Not .TakeFocusOnClick Then
.TakeFocusOnClick = True
.SetFocus
End If
End With
End If
' now CmdExit is the ActiveControl
MsgMe "Cmd Exit: ActiveControl = " & ActiveControl.Name
Me.Hide
End Sub

Using pstimer to run an event

I want to execute a function or a procedure or a block in regular intervals of 60seconds.
Do i have to use PS timer for that?
let this be my block
MyString = myEditor.Screen-Value.
Message MyString.
//myEditor is my Editor widget in the frame.
//My String is a string which i will use to display
I want to repeat this in each 60 seconds.. So that everytime it should display whatever I typed
inside the editor.? How can I do this with using PSTimer or without using it?
You must generate a Tick trigger block.
In this block can you write your code.
PSTimer has a property "Interval", which means interval for Tick event in miliseconds.
It has also other property "Enabled", witch starts and stopts timer.
When you run some longer code from your Tick event, schould be better switch that property "Enabled" on FALSE and on end of the trigger over on TRUE. Otherwise you can become a conflict with your code and new Tick event resp. your program will making never other, only showing runing your trigger script.