Progress 4gl-internal procedure - progress-4gl

Is there a way to use the variable of a procedure(.p file) in its internal procedure (.p file)?

The actual problem seems to be: "I have many procedures that call a common procedure. In one special case I need to have the common procedure set a special value in the calling procedure but I cannot modify all of the others."
The simple, but architecturally repugnant, solution is to use a global shared variable.
Many people will tell you that this is a bad coding technique. They are right. But you aren't asking for advice on best practices.
Simply create such a variable in both the caller and the callee. Procedures that don't need it won't miss it.
One of your "normal" programs:
/* p1.p */
message "p1, I'm normal.".
run common.p.
Your "special" program:
/* p2.p */
define new global shared variable special as character no-undo.
message "p2, I'm special!".
run common.p.
message "special = " special.
The common program:
/* common.p */
define new global shared variable special as character no-undo.
message "common stuff...".
if program-name(2) = "p2.p" then special = "special value".
return.
You can define a NEW GLOBAL SHARED variable as many times as you like and you only get one copy of it. The "new" doesn't overwrite any existing variables (if you leave out GLOBAL it behaves differently).
You didn't ask for it and maybe you don't need it but the program-name(2) check peeks at the call stack to see if common.p was called by p2.p.
There are other, more complicated ways to do this but they all boil down to the same problem -- you're creating the basis for some very ugly coupling between your "special" program and the now no longer generic "common" program.

Like this?
define variable i as integer no-undo. /* a variable in the main procedure */
procedure internalProcedure:
message "Internal Procedure (a):" i.
i = 13.
message "Internal Procedure (b):" i.
end.
/* main block
*/
i = 1.
message "Main procedure (a):" i.
run internalProcedure.
message "Main procedure (b):" i.
The more difficult question might be: "Is there anyway to prevent a globally scoped variable from being used in an internal procedure"?

Is this what you mean?
Here we have a common .p file called ShowTime.p that the other .p files will run. You want it to behave in a certain way for one of the .p files, and a different way for the others. For this reason we define a parameter. In this example it is a LOGICAL parameter, so the value can be YES or NO. As you can see, the ShowTime procedure will display the TIME if the lShowDate parameter is NO, and it will display the DATE if the lShowDate parameter is YES.
/* ShowTime.p */
DEFINE INPUT PARAMETER lShowDate AS LOGICAL.
IF lShowDate = NO THEN
MESSAGE STRING(TIME,"HH:MM:SS").
ELSE
MESSAGE STRING(TODAY,"9999/99/99").
Here is one of your five unrelated .p files. It runs the ShowTime.p procedure, passing it a parameter of NO, so ShowTime will show the TIME.
/* p1.p */
MESSAGE "P File 1".
RUN ShowTime(INPUT NO) NO-ERROR.
Another one, why not?
/* p2.p */
MESSAGE "P File 2".
RUN ShowTime(INPUT NO) NO-ERROR.
This .p file is running ShowTime with the lShowDate parameter as YES, which means that the ShowTime procedure will behave differently for this one, by showing the DATE instead of the TIME.
/* p5.p */
MESSAGE "P File 5".
RUN ShowTime(INPUT YES) NO-ERROR.
So basically what you have is one procedure, ShowTime, that can do one of two things. An unrelated procedure that runs this one procedure can tell it what it wants, by passing it a parameter.
To put it in terms of your question: there are five procedures (p1.p, p2.p ... p5.p) which are not related to each other, sharing a single procedure (ShowTime.p file). The shared procedure is modified with a parameter, so that it can have its effect on only one calling procedure (the one with the YES parameter) and not the other four (the ones with the NO parameter).
But if you cannot change the way five unrelated procedures run the shared procedure, if you can only change the shared procedure, you have to make your decision on something other than a parameter. What about this:
/* DoThing.p */
IF SOURCE-PROCEDURE:NAME = "p5.p" THEN
MESSAGE "Do one thing".
ELSE
MESSAGE "Do something else".
This will do one thing for one of the five procedures, and something else for the other four.

Related

AppleScript pass expression into function to be re-evaluated repeatedly? (or: AppleScript handler with callback?)

I think the correct description for what I'm trying to do is be able to pass an expression or function/handler into another handler as a parameter/argument. Some code to be evaluated inside the receiving handler. Similar to Javascript callbacks, I think.
For example, something like this:
on waitFor(theConditionExpression)
timeout_start(5) -- start a 5 second timer
repeat until (theConditionExpression or timeout_isExpired())
delay 0.1
end repeat
return theConditionExpression
end waitFor
theConditionExpression should be some expression or function that evaluates to a boolean result.
not really relevant to the question, but just FYI, timeout_start(…) and timeout_isExpired() are two simple handlers I've written that do exactly what they say. (…start() doesn't return anything, …isExpired() returns a boolean).
Of course, typically if I pass in some boolean expression, it will evaluate that expression once, at the time I pass it in. But I want it to evaluate it every time it's referenced in the code inside the handler.
Some languages (not sure about AS) have some kind of eval() function that you can pass it some code as a string and it will execute that string as code. Theoretically that could solve this, but: (a) I don't know if AS has anything like that, but even if it does, (b) it's not desired for various reasons (performance, injection risks, etc.)
So I'm thinking something more like eg. JavaScript's ability to pass in a function (named or anonymous) as function parameter/argument that can be re-evaluated every iteration in a loop, etc. (eg. like the compareFn argument in JS's Array.sort(compareFn)).
Can AS do anything like this, and if so how?
Thanks!
I'm going to suggest (pro forma) that an AppleScript application with an on idle handler is generally a better solution for wait conditions than a repeat/delay loop. It's more efficient for the system, and doesn't freeze up the script. But that would involve reconceptualizing your script, and I'm not certain it would work in this case, given the way you formed the problem.
There's an old but good site called AppleScript Power Handlers that shows a bunch of nifty-neato tricks for sophisticated use of AppleScript handlers: passing handlers as values or parameters; creating Script Objects within handlers; making closures and constructors. I'm pretty sure the answer to your request is in there. aLikely you'll want to set up a bunch of handlers that serve as condition expressions, then pass them as parameters to the evaluating handler. Or maybe you'll want to set up a script object containing the condition handlers and call it as needed?
At any rate, see what you can do with it, and ask more specific questions if you run into problems.

Is that able to have a transaction-wise global variable in PostgreSQL?

The situation is that: I have a function F1 which write into a buffer and the buffer will be write to external files when the function F1's fcinfo->flinfo->fn_mcxt is released; I also have a function F2 which depends on those external files, so when it runs, I want to make sure that all existing function F1's buffer (in this trasaction) have already write out to the external files. The two functions are independent, except when they are performed together.
As the result, I want that buffer to be a global variable in this transaction, so F2 can check it and decide if it is empty. If it is not empty, F2 can write it out manually.
Since PostgreSQL uses multiprocessing, one backend cannot see the global variables in another backend. You could write a _PG_init function that creates a shared memory segment for that purpose (see pg_stat_statements). That requires that your library is added to shared_preload_libraries.
A simpler alternative might be to use the LISTEN / NOTIFY facility of PostgreSQL to synchronize different backends.

How to insert page break through the program in maple?

How should we insert a page break via the code itself in maple program ?
Not insert page break? of menu
As I want to insert the page break as the code runs and with other outputs. In maple code.
Let's suppose that you have the following PutGraph procedure defined and available.
(You could define it in the Startup Code region of your Document/Worksheet. Or you could define it in your Maple initialization file. Or you could define it and store it in a .mla Library archive within your libname path. Or you could simply define it at the start of any Document/Worksheet in which you want to utilize it. I leave that choice to you.)
Then you can utilize this Putgraph procedure instead of DrawGraph, with the extra effect that it inserts a pagebreak right before the inlined plot of the Graph.
It also accepts a 2D or 3D plot as its argument. And if its argument is a Graph then you can also pass additional arguments that would normally be accepted by DrawGraph.
The restriction is that you can only effectively use one such call to this implementation of PutGraph per Execution Group or Document Block. If you want it done otherwise then you will have to show me explicitly how you need to call it once for multiple insertions. If you want specific behaviour then you will have to describe it adequately.
Here's the procedure, as 1D Maple notation code:
PutGraph:=proc(GG::{Graph,specfunc({PLOT,PLOT3D})})
local T,xml;
uses DocumentTools,DocumentTools:-Layout,GraphTheory;
if GG::Graph then
T:=DrawGraph(GG,_rest);
else T:=GG; end if;
xml:=Worksheet(Group(Input(Textfield(InlinePlot(T)))));
InsertContent(subsindets(xml,'specfunc(anything,_XML_Input)',
u->':-_XML_Input'(':-_XML_Pagebreak'(),
op(u))));
return NULL;
end proc:
Here's some example of calling it. (I'll mention again: it needs to be called at most once per Execution Group or Document Block. If you put it more than once in the same Execution Group/Block then only the last instance will work as intended.)
G1 := GraphTheory:-RandomGraphs:-RandomGraph(6,degree=3):
Now, in its own Execution Group/Document Block,
PutGraph(G1);
You can have something else appear before the next page break, or several things like text, other input/output, etc.
somethingelse;
And now, in its own Execution Group/Block,
PutGraph(G1,showlabels);
And so on,
somethingelse;
PutGraph(G1,showweights);
somethingelse;
P := GraphTheory:-DrawGraph(G1):
PutGraph(P);
somethingelse;

How to find the procedure handle of the calling procedure?

I'm trying to write some testing programs and I have an idea to run a procedure, and have the procedure call internal functions and procedures in the calling program. As I understand it, you can use RUN x IN y where y is any procedure in the calling stack.
However, I can't seem to find any way of getting the handle for the calling procedure. There doesn't seem to be a means of moving up the call stack.
The only way I can think of doing it is by passing the calling procedure's THIS-PROCEDURE handle as a parameter. It would work, but seems rather inelegant.
You are almost there. Take a look at the example below utilizing the THIS-PROCEDURE:INSTANTIATING-PROCEDURE handle.
INSTANTIATING-PROCEDURE attribute
Returns the handle to the procedure in which an object was instantiated.
parentProgram.p
RUN childProgram.p.
PROCEDURE hello:
DEFINE INPUT PARAMETER pcMessage AS CHARACTER NO-UNDO.
MESSAGE "Child says:" pcMessage VIEW-AS ALERT-BOX INFORMATION.
END.
childProgram.p
MESSAGE "Calling parent. Anybody home?" VIEW-AS ALERT-BOX.
IF VALID-HANDLE(THIS-PROCEDURE:INSTANTIATING-PROCEDURE) THEN
RUN hello IN THIS-PROCEDURE:INSTANTIATING-PROCEDURE (INPUT "I am your child") NO-ERROR.
IF ERROR-STATUS:ERROR THEN DO:
MESSAGE "That didn't work" VIEW-AS ALERT-BOX ERROR.
END.
To get the handle of the calling procedure, use the SOURCE-PROCEDURE handle. It has the handle for the last procedure call. If another procedure is run, then it will change to match that procedure instance's handle, so if you need your program to remember who called it, your code'll need to store that value immediately when it's called.

Determining direct-feedthrough paths without compilation/execution

I am currently working on a tool written in M-Script that executes a set of checks on a given simulink model. This tool does not compile/execute the model, I'm using find_system and get_param to retrieve all the information I need in order to run the routines of my tool.
I've reached a point where I need to determine whether a certain block has direct-feedthrough or not. I am not entirely sure how to do this. Two possible solutions come to mind:
A property might store this information and might be accessible via get_param. After investigating this, I could not find any such property.
Some block types have direct-feedthrough (Sum, Logic, ...), some other do not (Unit Delay, Integrator), so I could use the block type to determine whether a block has direct-feedthrough or not. Since I'm not an experienced Simulink modeller, I'm not sure if its possible to tell whether a block has direct-feedthrough by solely looking at its block type. Also, this would require a lookup table including all Simulink block types. An impossible task, since additional block types might get added to Simulink via third party modules.
Any help or pointers to possible solutions are greatly appreciated.
after some further research...
There is an "official solution" by Matlab:
just download the linked m-file
It shows that my idea was not that bad ;)
and for the record, my idea:
I think it's doable quite easily. I cannot present you some code yet, but I'll see what I can do. My idea is the following:
programatically create a new model
Add a Constant source block and a Terminator
add the Block you want to get to know the direct feedthrough ability in the middle
add_lines
run the simulation and log the states, which will give you the xout variable in the workspace.
If there is direct feedthrough the vector is empty, otherwise not.
probably you need to include some try/catch error catching for special cases
This way you can analyse a block for direct feedthrough by just migrating it to another model, without compiling your actual main model. It's not the fastest solution, but I can not imagine that performance matters that much for you.
Here we go, this script works fine for my examples:
function feedthrough = hasfeedthrough( input )
% get block path
blockinfo = find_system('simulink','Name',input);
blockpath = blockinfo{1};
% create new system
new_system('feed');
open_system('feed');
% add test model elements
src = add_block('simulink/Sources/Constant','feed/Constant');
src_ports = get_param(src,'PortHandles');
src_out = src_ports.Outport;
dest = add_block('simulink/Sinks/To Workspace','feed/simout');
dest_ports = get_param(dest,'PortHandles');
dest_in = dest_ports.Inport;
test = add_block(blockpath,'feed/test');
test_ports = get_param(test,'PortHandles');
test_in = test_ports.Inport;
test_out = test_ports.Outport;
add_line('feed',src_out,test_in);
add_line('feed',test_out,dest_in);
% setup simulation
set_param('feed','StopTime','0.1');
set_param('feed','Solver','ode3');
set_param('feed','FixedStep','0.05');
set_param('feed','SaveState','on');
% run simulation and get states
sim('feed');
% if condition for blocks like state space
feedthrough = isempty(xout);
if ~feedthrough
a = simout.data;
if ~any(a == xout);
feedthrough = ~feedthrough;
end
end
delete system
close_system('feed',1)
delete('feed');
end
When enter for example 'Gain' it will return 1, when you enter 'Integrator' it will return 0.
Execution time on my ancient machine is 1.3sec, not that bad.
Things you probably still have to do:
add another parameter, to define whether the block is continuous or discrete time and set the solver accordingly.
test some "extraordinary" blocks, maybe it's not working for everything. Also I haven implemented anything which could deal with logic, but actually the constant is 1 so it should work as well.
Just try out everything, at least it's a good base for you to work on.
A famous exception is the StateSpace Block which can have direct feedthrough AND states. But there are not sooo much standard blocks with this "behaviour". If you also have to deal with third party blocks you could get into some trouble, I have to admit that.
possible solution for the state space: if one compares xout with yout than one can find another indicator for direct feedthrough: if there is, the vectors are not equal. If so, than they are equal. Just an example, but I can imagine that it is possible to find more general ways to test things like that.
besides the added simout block above one needs the condition:
% if condition for blocks like state space
feedthrough = isempty(xout);
if ~feedthrough
a = simout.data;
if ~any(a == xout);
feedthrough = ~feedthrough;
end
end
From the documentation:
Tip
To determine if a block has direct feedthrough:
Double-click the
block. The block parameter dialog box opens.
Click the Help button in
the block parameter dialog box. The block reference page opens.
Scroll
to the Characteristics section of the block reference page, which
lists whether or not that block has direct feedthrough.
I couldn't find a programmatic equivalent though...
Based on a similar approach to the one by #thewaywewalk, you could set up a temporary model that contains an algebraic loop, similar to,
(Note that you would replace the State-Space block with any block that you want to test.)
Then set the diagnostics to error out if there is an algebraic loop,
If an error occurs when the model is compiled
>> modelname([],[],[],'compile');
(and you should check that it is the Algebraic Loop error that has occured), then the block has direct feed though.
If no error occurs then the block does not have direct feed though.
At this point you would need to terminate the model using
>> modelname([],[],[],'term');
If the block has multiple inports or outprts then you'll need to iterate over all combinations of them.