I create an integer to count the number of assemblies (e.g. countAssembler)
On exit from FA1 (countAssembler++;)
Then I have an event triggered by a condition such that when the count of assemblies reaches 10 ((countAssembler==10)), it suspends the FA1 for two hours using suspend function.
But how do I implement the suspend function? Do you have any ideas?
note that the suspend function doesn't suspend the assembler, but an item that is currently being processed in the assembler...
have a variable countAssembler count the number of items produced... then
on exit you write countAssembler++;
on enter delay you write the following:
if(countAssembler==10){
self.suspend(agent);
create_MyDynamicEvent(2, HOUR,agent);
}
on the dynamic event you write:
assembler.resume(agent);
Don't forget to add the parameter needed in the dynamic event:
Note that the suspend will start when part 11 starts being assembled, which means that the machine will be suspended for 2 hours + the time between the 10th assembly end and then 11th assembly start... you can fix that easily but im not including it in my answer
Related
The "Assembler" should stop working for 2 hours after 10 assemblies are done.
How can I achieve that?
There are so many ways to do this depending on what it means to stop working and what the implications are for the incoming parts.. but here's one option
create a resourcePool called Machine, this will be used along with the technicians:
on the "on exit" action of the assembler do this (I use 9 instead of 10 because the out.count() doesn't count until the agent is completely out, so when it counts 9, it means that you have produced 10)
if(self.out.count()==9){
machine.set_capacity(0);
create_MyDynamicEvent(2, HOUR);
}
In your dynamice event (that you have to create) you will add the following code:
machine.set_capacity(1);
A second option is to have a variable countAssembler count the number of items produced... then
on exit you write countAssembler++;
on enter delay you write the following:
if(countAssembler==10){
self.suspend(agent);
create_MyDynamicEvent(2, HOUR,agent);
}
on the dynamic event you write:
assembler.resume(agent);
Don't forget to add the parameter needed in the dynamic event:
Create a variable called countAssembler of type int. Increment this as agents pass through the assembler. Also create a variable called assemblerStopTime. You also record the assembler stop time with assemblerStopTime=time()
Place a selectOutputOut block before the and let them in if countAssembler value is less than 10. Otherwise send to a Wait block.
Now, to maintain the FIFO rule, in the first selectOutputOut condition, you need to check also if there is any agent in the wait block and if the current time - assemblerStopTime is greater than 2. If there is, you free it and send to the assembler with wait.free(0) function. And send the current agent to wait. You also need to reset the countAssembler to zero.
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
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
I would like the workflow to run for 20 minutes....If the running process does not complete within 20 minutes, the workflow should be ended immediately..However, I can only find the timer, but it is used for starting the process after the indicated time which is not I'm looking for...Does anyone know how to specify the duration for the workflow?
[updated to cover complete scenario and cover issues raised by Koushik Sinharoy in comments]
You can achieve it using the timer:
Link one to the Start task and set it to run for 20 minutes in
parallel with your session.
Have a Decision task with Treat the input links as set to OR. This will trigger the decision whenever any of the preceeding task ends, so either your session will get completed or timer runs out (whatever happens first).
Set the Decision condition to $s_your_session.Status = SUCCEEDED.
Link the Decision task to Control Task.
Set Control task to Fail parent.
Add a condition $Decision.Condition = False to the link between Decision task and Control task.
This should be the result:
Start--->s_your_session--\
\ > Decision [OR] ---(False)---> Control Task [Fail parent]
\-->timer-----------/
Thanks Koushik Sinharoy for the remarks below!
I am working on fire evacuation of a building floor and would like to count the number of people remaining inside the building after 120 seconds? The timer should start once the evacuation process begins, which is by alarm that goes off after a certain amount of time using an event feature.
I know how to count the total number of people inside the building using the function component getPeopleInsideCount and a text with getPeopleInsideCount(). But I don't know what code to use for my problem.
Below is the code:
return pedOffice.countPeds() + pedStudents.countPeds() - pedSink.sink.count();
With this, it will count the people in the building floor and it will stop counting after 120 seconds...
Step 1:
create an event with trigger type timeout, and mode: user control, and timeout=120 seconds.
Step 2:
create a variable called stopCounting as a boolean with initial value false
create a variable called peopleRemaining as an int
Step 3:
when the evacuation begins run the code:
event.restart();
Step 4:
in your event use the following code:
stopCounting=true;
peopleRemaining=getPeopleInsideCount();
Step 5
In your text use the following code instead of getPeopleInsideCount()
stopCounting ? peopleRemaining : getPeopleInsideCount()
Add a dynamic event that returns the count you need.
Once your alarm goes off, you can call that dynamic event 120 seconds later using create_MyDynamicEvent(120, SECOND);
That will execute the event code 120 seconds later.
cheers