Getting Updated Value while using lock? - progress-4gl

this is my first Procedure.
define frame LockFrame Customer.Name Customer.CreditLimit Customer.Balance.
pause.
DO TRANSACTION:
for each Customer exclusive-lock:
assign Customer.CreditLimit = Customer.CreditLimit + 5.
pause 1 no-message.
display Customer.Name Customer.CreditLimit Customer.Balance.
end.
end.
and thsi is my second Procedure.
define frame LockFrame Customer.Name Customer.CreditLimit Customer.Balance.
pause.
DO TRANSACTION:
for each Customer exclusive-lock:
assign Customer.Balance= Customer.Balance + 2.
pause 1 no-message.
display Customer.Name Customer.CreditLimit Customer.Balance.
end.
end.
When I run first Procedure and just after second procedure, I have to get the value Updated by first (here CrditLimit).(and vice versa)
But I am not able to run second since the record is locked by the first.It is showing an error message.
I think problem is with my locking.Please help on this.

This is exactly what you should expect.
The record is locked, exclusively for the benefit of the first procedure, until the transaction commits. The commit will occur at the 2nd END statement.
I'm not sure why you have PAUSE in there -- you should never block on IO inside a transaction block -- that will only lead to problems (like users locking each other while they get up and go get coffee...)
You almost NEVER really want to enclose an update of an entire table in a transaction (which is what your DO TRANSACTION block is doing). Even if you think that is what you want to do, or have been told that that is what you must do it is probably wrong. This is usually the result of confusing a "business transaction" with a "database transaction" -- they are not the same thing -- especially when large amounts of data are in play.
A better way to write your code (apply the same concept to both samples):
define buffer updCustomer for customer.
for each customer no-lock /* where whatever */:
/* maybe some no-lock logic... */
do for updCustomer transaction:
find updCustomer exclusive-lock where recid( updCustomer ) = recid( customer ).
assign
updCustomer.creditLimit = customer.creditLimit + 5.
.
display
updCustomer.name
updCustomer.creditLimit
updCustomer.balance
.
end.
pause 1 no-message.
end.
Using NO-LOCK on the FOR EACH allows selection logic or other logic to run without needing the lock. Using the update buffer and the DO FOR ... TRANSACTION block tightly scopes the record lock and the transaction to that single block. Placing the PAUSE outside the block prevents the "user goes to get coffee" problem.

I hesitate to comment on any answer that Tom has given, since I consider him authoritative. But I want to point out two small things.
First of all, the use of RECID might be better as ROWID. ROWID is recommended for use by Progress (see http://documentation.progress.com/output/OpenEdge102a/oe102ahtml/wwhelp/wwhimpl/common/html/wwhelp.htm?context=dvref&file=dvref-15-48.html) since RECID is "supported for backward compatibility. For most applications, use the ROWID function, instead."
But that's minor, really. What's also important in my opinion is what Tom did in his example for you - he defined a buffer ("define buffer updCustomer for customer.") that he used in the update. I want to encourage you to use buffers EVERY time you work with a record, especially if you get into using persistent or super procedures, or if you are using functions or internal procedures.
Why? Defining a buffer ensures that the scope of the buffer you are updating is limited to the place where you defined it. As an example, Progress will "leak" the default buffer into your super procedure if you aren't careful. Imagine this scenario...a program that finds a record, calls a function in a super procedure to do "some stuff" and then deletes the record.
FIND MyTable WHERE MyTable.fk = fkValue NO-LOCK NO-ERROR.
UpdateOtherStuff(MyTable.fkValue).
DeleteMyRecord(MyTable.fkValue).
But in "UpdateOtherStuff", it does some work including this...
FOR EACH MyTable:
If MyTable.Thing = 'ThingOne' THEN LEAVE.
/* other processing here... */
END.
You might be surprised when you find that the super procedure shares the default "MyTable" buffer with your program, and ends up repositioning the record somewhere you don't want...so that the call to "DeleteMyRecord()" has a different record than you expect.
The problem would be solved if "UpdateOtherStuff" had a "DEFINE BUFFER ... FOR MyTable" at the top, even if it was "DEFINE BUFFER MyTAble for MyTable" (strange as that looks...).
That's why Tom's example, including a DEFINE BUFFER..., should be a template for the work you do in the ABL.
This question was asked previously - see https://stackoverflow.com/a/5490130/1433147.

Related

How to make condition for queue?

I want to make agents to not enter the queue if it's full (if it's full then go to sink) at the moment of arrival in selectOutput5.
I tried to put "if-else" into "Actions" section
But I don't really know which parameter to use (tried to use queue.size and queue.capacity but I don't know how to code this properly), please help. Not sure if I doing the right thing at all by trying to put if-else into actions of selectOutput5
The model look like this:
You need to code the conditions under which they should enter which queue and then if the conditions are not met it will assess the next out option.
See example below
It will only go to queue 1 if the queue size is less than 5, else it will assess the queue size of queue 2 and if both are full then go to the exit.
The Actions section is only for code you want to execute if they do exit one of the out options.
Put a selectOutputOut block, select conditions there. And type below your condition: yourQueue.size()>=yourQueue.capacity(). Send the ones to Sink block when this condition is true.

Anylogic - Assembler should stop working for 2 hours after 10 assemblies done

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.

How to avoid an event being overwritten by a general one?

I have a main application, with following event:
ON ESCAPE ANYWHERE
I've just created a frame within a window, where I'd like to add an event:
ON ESCAPE ...
This, however, seems not to work as the main application already has an ON ESCAPE ANYWHERE. Is there something like ON ESCAPE OVERWRITES DEFAULT to create an event for my subwindow?
Thanks in advance
From the documentation notes:
A trigger defined with the ON statement remains in effect until one of
the following occurs:
Another ON statement defines another trigger (or REVERT) for the same event and widget
For a non-persistent trigger, the procedure or trigger block in which the ON statement appears terminates
So I think your main application trigger is defined after your own trigger, overwriting it. If you can be specific enough, you should be fine:
on escape anywhere do:
message 'escaping from anywhere' view-as alert-box.
end.
define frame fr1
cc1 as char
cc2 as char
with side-labels
.
do with frame fr1:
enable all.
on escape of cc1 do:
message "escaping from cc1 in frame 1" view-as alert-box.
end.
end.
view frame fr1.
wait-for close of frame fr1.

Confusion regarding usage of event.triggered

I'm trying out some code that is supposed to block until moving to a new simulation time step (similar to waiting for sys.tick_start in e).
I tried writing a function that does this:
task wait_triggered();
event e;
`uvm_info("DBG", "Waiting trig", UVM_NONE)
-> e;
$display("e.triggered = ", e.triggered);
wait (!e.triggered);
`uvm_info("DBG", "Wait trig done", UVM_NONE)
endtask
The idea behind it is that I trigger some event, meaning that its triggered field is going to be 1 when control reaches the line with wait(!e.triggered). This line should unblock in the next time slot, when triggered is going to be cleared.
To test this out I added some other thread that consumes simulation time:
fork
wait_triggered();
begin
`uvm_info("DBG", "Doing stuff", UVM_NONE)
#1;
`uvm_info("DBG", "Did stuff", UVM_NONE)
end
join
#1;
$finish(1);
I see the messages Doing stuff and Did stuff, but Wait trig done never comes. The simulation also stops before reaching the finish(1). One simulator told me that this is because no further events have been scheduled.
All simulators exhibit the same behavior, so there must be something I'm missing. Could anyone explain what's going on?
The problem is with wait (!e.triggered); when e.triggered is changing from 1 to zero. It has to change in a region where nothing can be scheduled, so whether it changes at the end of the current time slot, or the beginning of the next time slot is unobservable. So the wait will hang waiting for the end of the current time slot, which never comes.
I think the closest thing to what you are looking for is #1step. This blocks for the smallest simulation precision time step. But I've got to believe there is a better way to code what you want without having to know if time is advancing.

UPDATE and READKEY at the same time?

In my application I'm building, I'd like for the first screen to be an UPDATE to an integer, but I'd like to have an option to press F2 to access a different kind of functionality in the program.
When I try it the logical way, I get buzzed at since the UPDATE is expecting INTEGER only input, and I'm pressing F2.
Can you UPDATE and READKEY successfully at the same time?
You're trying to do it the old way with editing blocks - try reading up on event-driven programming, where the code describes events and what gets run when a certain event happens. The code would look something like this:
ON F2 of update-field
DO: /* something */
END.
UPDATE update-field.
Better yet, don't use "UPDATE", do a "WAIT-FOR" instead.