Showing posts with label Calling a procedure from an external application. Show all posts
Showing posts with label Calling a procedure from an external application. Show all posts

Thursday, 16 June 2022

Winrun redux

Several months ago, I wrote about using the program WINRUN in order to send data to Priority from a computer that is not running Priority. I wrote a program in Delphi that would accept barcodes (as read from a dumb barcode reader) and then would transmit them to Priority, invoking a procedure that could handle these barcodes. By the time I wrote that blog entry (in November), the Delphi program was working as designed.

That is, working until about a month ago, when we upgraded our version of Priority from 18.2 to 21.1 (a giant step that caused all kinds of problems). At first it seemed that the only change necessary to my program was to enclose the procedure name with quotation marks, but unfortunately this transpired to be not true. The program would work on a computer that was running Priority but would not work on a computer that was connected to the network and could access the Priority directories but was not running Priority.

With hindsight and after rereading the documentation, I could see that the problem was with accessing the tabula.ini file. Documented was a change in the environmental variable required for the program. Originally the variable was called TABULAINI and as I wrote then, this only has to be done once on the computer. In the mean time, the name of this variable has been changed to TABULAINIORIG (one might ask why this was done; I presume that the answer would be 'davka', a wonderful Hebrew word that supposedly means 'precisely', but has the connotation of being contrary).

Even after I changed the value of the environmental variable, my program still would not work. I had noticed before that the date of the file tabula.ini would change every time that it was accessed, but it hasn't changed since the end of May. This no doubt explains why the program doesn't work. But why?

Today I had the insight to run the SET command (without parameters) on the computer: this lists all the environmental variables that had been defined on the computer. TABULAINIORIG was not there. I have been using a batch file to check WINRUN, and indeed the first command was SET TABULAINIORIG = x:\1\tabula.ini as per the book. I added the variable again, and after unsuccessfully running the batch file, I ran SET again - and the variable that I had just added had disappeared! It seems that WINRUN 'eats' this environmental variable which is really strange behaviour.

As by this time I had removed the SET command from the batch file, I typed it in manually. Then I ran the batch file - and it worked! I ran SET again and saw that now there was a variable tabulainiorig as well as TABULAINIORIG; it seems that WINRUN wants its environmental variable in lower case!

I changed the Delphi program so that instead of setting the (wrong) environmental variable only once at program start-up, the two variables are set immediately prior to running WINRUN (I'm not taking a chance with the upper/lower case problem). And now the program works again.

Friday, 12 November 2021

Executing Priority Commands from an External Application

This is another topic that is documented in the SDK (in chapter 15, advanced programming tools), but as always, what is written is somewhat ambiguous and a few more examples would not have hurt. After having spent no small amount of time figuring out what works, I want to record my experiences here.

But first of all: why do I need to execute a Priority command from an external application? Lately we've been working on raising the level of computerisation on the factory floor by means of barcode readers. My original concept was that there would be a computer running Priority with a special, private and bespoke, screen being displayed, into which barcodes are scanned. The screen has a post-insert trigger for the lines that does something useful and clever.

The next step came from someone asking why this computer has to be running Priority? There are both advantages and disadvantages to this: maybe the disadvantages can be overcome without losing the advantages? So I suggested writing a program in Delphi that accepts the barcodes, saves them to a file and then transfers that file to the server where it is read by a procedure that is activated every half an hour.

An even better solution would be that the computer running a program in Delphi would accept a barcode and transmit it immediately to Priority, possibly handling that barcode at the same time. I had an arrangement like this twenty years ago! It's called Remote Execution, or Rexec.

In a part of the SDK that I had managed to miss was the section that I needed: the calling computer runs a program called WINRUN on the server that runs WINACTIV that runs a procedure (this is only one of the possibilities documented). As I wrote at the beginning, the documentation is ambiguous and I want to explain it fully here.

First off: TABULA.INI. I found the copy of this file that is stored on the SQL server (i.e. the copy that is called when Priority is run on the server) - it's stored in C:\WINDOWS of the server and so is not normally visible. On our server there is a directory D:\pr_SQL that has subdirectories BIN.95, system, etc - i.e. Priority; each installation chooses its own directory for this. This directory is mapped as X: on client computers; on the server I copied c:\windows\tabula.ini to x:\1\tabula.ini, making this file visible to all. On the client computer, where my Delphi program will be run, I defined an environmental variable thus: set TABULAINI=x:\1\tabula.ini. This only has to be done once on the computer.

I then wrote a procedure in Priority, TEST_UPDCONST. This procedure simply updates a value in a private table whenever it is run - this way I could see whether what I was doing was working. Once I had the command debugged, I then investigated how I could pass parameters to the procedure (this obviates the need to transfer files or similar).

Here is the final command that must be sent to Priority, along with comments.

Command part Description Constant
X:\BIN.95\WINRUN Winrun is the program that is run on the server; it is situated at X:\BIN.95 on my server. The location should be BIN.95 on the drive where Priority is located. Y
""
Two double quotation marks. Don't ask me why. Y
tabula 123456
The account name and password. It makes sense to have tabula run the procedure, thus preventing any problem with permissions. Of course, the password is not 123456.
x:\system\prep
This directory is always used when preparing procedures. Y
demo
The name of the company (environment) in which the command will be run
X:\BIN.95\WINACTIV.EXE This is the name of the program that runs the procedure. It didn't work without prefacing the name with its location. Y
-P A procedure is being run Y
TEST_INSERTNESTBC This is the name of the procedure
-var:BC "A09" First variable that is being passed to the procedure, whose name is BC and whose value is "A09"
-var:TS "12/11/21 09:04" Second variable that is being passed to the procedure, whose name is TS and whose value is "12/11/21 09:04" (i.e. the current date and time)

Put together, the complete command is X:\BIN.95\WINRUN "" tabula 123456 x:\system\prep demo X:\BIN.95\WINACTIV.EXE -P TEST_INSERTNESTBC -var:BC "A09" -var:TS "12/11/21 09:04".

In the above table, there is a column 'constant'; if the value is Y, then the command part should be written as is (obviously substituting the correct directory on the server). The SDK does not mention that the parameters of the command are case sensitive: if one is passing parameters to the procedure being called, then one must write "-var" and not "-VAR" as I originally did. One optional parameter is "err <filename>": this causes error messages from the called procedure to be written in this file; again, it's "err" in lower case, not "ERR".

The SDK also documents how the called procedure should accept its parameters; I thought that this was ambiguous but turns out to be accurate. In the case shown above, the called procedure (TEST_INSERTNESTBC) has two char parameters, BC and TS. Inside the procedure, they are referred to as :EXTERNAL.BC and :EXTERNAL.TS, not :$.BC and :$.TS. My misunderstanding was with reference to 'EXTERNAL'; in chapter 2 of the SDK there is a list of predefined variables, such as :PAR1, :PAR2, :PAR3 and :KEYSTROKES. I've noted in this blog a few undocumented variables to add to this list. I did not notice that the last variable appearing in the list is :EXTERNAL.VARNAME, "used in procedures to refer to variables inputted [sic] as part of the WINRUN command". This is similar to the half-documented variable :PROGPAR.VARNAME that I discussed here.

So now I have a very powerful tool at my disposal. First it will be used in a specific setting (that received its inspiration from another Delphi program that I developed this week that avoided having to access Priority), but I'm sure that in the coming months, this technique will be used more frequently.

It should be noted that this technique is s-l-o-w: it takes about five minutes for the entire process.