.PAPER SIZE 60,78 .DATE .ENABLE BOLD .FLAGS BOLD .LEFT MARGIN 5.RIGHT MARGIN 75 .TITLE #####A Replacement for the Corporate Telephone Directory .SUBTITLE #####Bart Z.#Lederman .CENTER ^&A Replacement for the ALL-IN-ONE Corporate Telephone Directory\* .BLANK.CENTER ^&using DATATRIEVE and FMS,\& .BLANK.CENTER ^&which may also be used ^*without\* ALL-IN-ONE.\* .BLANK 2.CENTER Bart Z.#Lederman .BLANK.CENTER 2572 E. 22nd St. .BLANK.CENTER Brooklyn, N.Y. 11235-2504 .NOTE Abstract .PARAGRAPH One of the functions supplied with ALL-IN-ONE V2 is a corporate telephone directory. I found that it's usefulness was very limited due to poor performance. I have developed this replacement for the corporate telephone directory which can read the same data file: this allows any information which has already been input to the telephone directory to be saved. The application uses VAX-DATATRIEVE and FMS to present the information in a menu-driven form, which allows better manipulation of the information. The application is also more easily adapted to individual requirements: in this example, a field for a third telephone number has been added, the state is recorded as a separate field, and the information may be entered in lower case so that it will have a better appearance when viewed, and when used for other purposes such as letters, mailing labels, etc. .PARAGRAPH Because the application now runs in DATATRIEVE, it may be used ^*^&without\&\* ALL-IN-ONE, as well as being accessed from within ALL-IN-ONE. .END NOTE .PARAGRAPH I first became acquainted with the AI1 telephone directory when I started my present job. The users had over 1800 entries already in the directory, but were not really able to use the information due to the poor performance of the application. When it was first demonstrated to me on a VAX-11/750 with ONE user, retrieval of the first entry took over 45 seconds, and all subsequent retrievals were equally slow. I decided to create a package which would perform the same or similar function: this application had to be done in a way that could be integrated into AI1 and would require a minimum of training for the users during conversion, and this in turn required the use of menus and forms in a manner similar to AI1. In addition, since a considerable amount of labor had already been expended in creating the 1800+ existing entries, the new application had to be able to read the existing data file. .BLANK 2.TEST PAGE 5.CENTER Why use DATATRIEVE? .PARAGRAPH I chose DATATRIEVE as it can implement this application with good performance, it takes relatively little time to develop applications, it allows for easy modification of the resulting application, it can read the existing data file, it can be called from AI1, and it works well with FMS (or TDMS) to provide the forms and menu driven interface. The fact that the DATATRIEVE application may also be easily accessed ^*^&without\&\* AI1 is an important asset in many situations, though it happened not to be important at this site at the present time. .BLANK 2.TEST PAGE 5.CENTER Why use FMS? .PARAGRAPH Although DATATRIEVE can just as easily work with TDMS, AI1 uses FMS so I also used it for my application. It is important to note that AI1 can be linked with only one DATATRIEVE image, and that image can in turn be lined with either FMS or TDMS but ^*^¬\&\* both; so one should decide which product is going to be used for all (or most) DATATRIEVE applications which will be integrated under AI1 before any are developed: this allows the proper DATATRIEVE image to be linked to AI1. Though you can get to DATATRIEVE from AI1 using scripts or DCL command procedures, and these may invoke any DATATRIEVE image, the fastest way to DATATRIEVE is to invoke the linked copy directly with the DTR command internal to AI1, and this may invoke only the image that was linked to AI1. Since AI1 uses FMS and DATATRIEVE can use FMS, it may also be cheaper to use FMS for both rather than having to buy TDMS: one may also develop FMS applications using the tools within AI1 which cannot be done with TDMS. .BLANK 2.TEST PAGE 5.CENTER Creating a compatible record definition. .PARAGRAPH The first step in a conversion project such as this is to create a record definition which matches the existing data file. This could easily be an entire paper in itself: I will simply state here that I used the DUMP utility to look at the existing data, and also looked a little at the form used by the existing application. This information gave me the length of each field, the type of information, and the total record length: with this information it is a very straightforward task to write a matching record definition. I then defined a domain that used this definition on the existing data file, which is OA$DATA:CORPHONE.DAT and looked at the data to see that I indeed had the fields correctly defined: I was also able to scan through the data much more easily than from within AI1 or with the DUMP utility, and get a better idea of how the fields were being used. I also found that there was some empty space in the file: it happened that some of it came just after the telephone numbers, so I used part of that space to store a third telephone number which we happened to need at this site. I also used the last three characters at the end of the field which stores the city to record the state separately (it's three characters rather than two as we have to send letters to Canada, Australia, and other foreign destinations). .PARAGRAPH At this point, I now have the essentials of a DATATRIEVE application. If the users could be given a day or so in the use of DTR, this is really all that would be needed to access the information. It happens, however, that the policy at this site is to have all applications pre-defined, with all user functions set in procedures: therefore, procedures for all common functions needed to be defined. In addition, the use of forms often makes the information easier to manipulate: when there are many fields, it is easier to read if the form writes it to an entire screen with titles rather than simply PRINTing it or LISTing it. When an entry has to be added or modified, the use of a form allows the user to move backwards and forwards through all of the fields in a record; the user can see all of the fields while working, and can check to see that they are all correct before committing the record; a simple STORE or MODIFY prompts for the fields in order and does not allow backing up if an entry is incorrect. There are, therefore, many benefits to the use of forms in an application, and forms are relatively easy to use with DATATRIEVE. .BLANK 2.TEST PAGE 5.CENTER Creating Forms. .PARAGRAPH This too could be the subject of an entire paper. I followed the basic layout of the AI1 form as I did not want there to be too much of a change visible to the users. By following the same basic layout, very little retraining was needed. Also, the same form was copied for display, add, modify and delete: unfortunately, the forms generally cannot be shared as some fields must be marked DISPLAY#ONLY on some forms but not on others: the same form is used for display and delete, however, as they are both basically display only. I referred to the FMS manuals very little, if at all: Bert Roseberry's papers on using FMS with DATATRIEVE were far more readable and informative. The FMS description is the hardest part: once the forms are defined their use within DATATRIEVE is actually quite simple. For example, the procedure that displays a form for modification shows just about everything that needs be done with forms in DATATRIEVE. Once a particular record is selected, the form is selected with a DISPLAY__FORM, then fields are filled in with PUT__FORM, then data is retrieved with GET__FORM. .BLANK.NO JUSTIFY.NO FILL FOR CORPHONE WITH CKEY STARTING WITH LOOKUP||"" AND MORE = "Y" MODIFY USING BEGIN DISPLAY__FORM MODPHONE IN OA$DATA:CORPHONE.FLB USING ! ! Fill in the form with the existing data. ! BEGIN PUT__FORM CKEY = CKEY PUT__FORM NAME = NAME PUT__FORM TITLE = TITLE PUT__FORM BUSINESS = BUSINESS PUT__FORM PERSONAL = PERSONAL PUT__FORM OTHER = OTHER PUT__FORM DEPARTMENT = DEPARTMENT PUT__FORM COMPANY = COMPANY PUT__FORM MAIL__STOP = MAIL__STOP PUT__FORM STREET = STREET PUT__FORM CITY = CITY PUT__FORM STATE = STATE PUT__FORM ZIP = ZIP PUT__FORM NODE = NODE PUT__FORM COMMENTS = COMMENTS PUT__FORM PROMPT = "Go on to next entry [Y/N] ?" END RETRIEVE USING BEGIN ! ! Pull back the modified data. ! NAME = GET__FORM NAME TITLE = GET__FORM TITLE BUSINESS = GET__FORM BUSINESS PERSONAL = GET__FORM PERSONAL OTHER = GET__FORM OTHER DEPARTMENT = GET__FORM DEPARTMENT COMPANY = GET__FORM COMPANY MAIL__STOP = GET__FORM MAIL__STOP STREET = GET__FORM STREET CITY = GET__FORM CITY STATE = GET__FORM STATE ZIP = GET__FORM ZIP NODE = GET__FORM NODE COMMENTS = GET__FORM COMMENTS MORE = GET__FORM REPLY ! ! The default is to continue with next entry. ! IF MORE = "" MORE = "Y" END END ! ! See if another entry is wanted. ! DISPLAY__FORM SHOWPHONE IN OA$DATA:CORPHONE.FLB USING BEGIN PUT__FORM PROMPT = "Modify another entry [Y/N] ?" END RETRIEVE USING BEGIN ANSWER = GET__FORM REPLY END END END-PROCEDURE .BLANK.JUSTIFY.FILL You will notice that I have chosen field names within my FMS form that match the field names in the record definition: this is not necessary, but it does make things much simpler to remember. One feature worth noting is the use of two general purpose fields, "PROMPT" and "REPLY". This lets me put whatever message I want on the screen (FMS is set to right justify it), and do what I want with the answer (which FMS has been set to convert to upper case). Here it is used to prompt the user to determine if this entry should be skipped, and again to determine if a different entry should be modified (answering Y here results in the user being prompted for another lookup key). .BLANK 2.TEST PAGE 5.CENTER Incorporation into the CDD and ALL-IN-1. .PARAGRAPH The complete DATATRIEVE application may be installed into the CDD by entering DATATRIEVE and invoking the CORPHONE.COM command file (or CORPHONE.OLD as described later): this has been set to go into a company wide dictionary CORPORATE, which is what I have used for data which will be used for the entire company. At this point, any DTR user may invoke the procedure MENU__PHONE to access the telephone directory. To add the application to AI1 requires changing one or more forms to invoke DTR. The important item is the DATA item in the form which invokes DATATRIEVE: the form DATABASE which is supplied with this package has a selection field CP which invokes the telephone directory. An examination of this form and the DATABASE.FLG descriptor will show how DTR is invoked directly from AI1. Note that this ^&requires\& that AI1 be linked with DTR: the AI1 installation manual shows how this is done, but all it requires is that when you build AI1 that you answer "yes" when you are asked if you want to link with DATATRIEVE. The default DATATRIEVE image DTRSHR is the one you will probably want. If you do not link AI1 to DATATRIEVE, then you will have to write a script or command file, following the examples in section 6.30 of the ^&ALL-IN-1 OFFICE MENU Application Programmer's Reference, Volume 2: Functions\& manual. Performance will probably not be as good as having AI1 linked to DTR. All of the forms needed by the DATATRIEVE application are included here as forms and in the library CORPHONE.FLB, which the application expects to find in the AI1 dictionary normally pointed to by the logical OA$DATA. .PARAGRAPH There are many benefits to having a complete path name when invoking the procedure. For example, the AI1 command which will be in the form's data field is .BLANK DTR :CDD$TOP.CORPORATE.MENU__PHONE .BLANK This also requires the READY and procedure invocations to include a full path name: I have included the path name CDD$TOP.CORPORATE in CORPHONE.COM where needed. If this is done, then the procedure will always be invoked regardless of the current dictionary or default for that user. This would be useful if users also had individual DATATRIEVE applications they wished to invoke from AI1. For example, this package could be copied to replace the personal telephone directory, in which case each user would probably have an individual CDD dictionary: the ALL-IN-1 LOGIN file or the users' individual LOGIN file would assign their default to their own dictionary, the command on the AI1 form to get that DATATRIEVE procedure would NOT have a path name (because all AI1 users will be invoking the same form) and would go to the users' individual dictionary, but the command on the AI1 form for the corporate directory would be as shown above so that the corporate wide directory would be obtained from any users' default directory. I do not expect to implement personal telephone directories, but they could be done the same way (and even share the same forms) as the corporate directory. .PARAGRAPH Including path names is also useful for accessing the directory without AI1, as a user might do from DCL. If you have defined a command to start DATATRIEVE like this one: .BLANK $ DTR*32 :== $SYS$SYSTEM:DTR32 .BLANK in your LOGIN.COM file (or more probably in SYS$MANAGER:SYLOGIN.COM), then you can invoke a DATATRIEVE procedure directly with a command such as: .BLANK DTR; :CDD$TOP.CORPORATE.MENU__PHONE .BLANK and go directly into the corporate telephone application main form without having to change your own default dictionary, or indeed knowing anything about DATATRIEVE. It is also possible to define a command to invoke this procedure so users could just type in a command such as CORPHONE in DCL and go straight to the application: .BLANK COR*PHONE :== $SYS$SYSTEM:DTR32 :CDD$TOP.CORPORATE.MENU__PHONE .BLANK 2.TEST PAGE 5.CENTER Other modifications. .BLANK Once you have switched to this version of the telephone directory, you may want to disable the version supplied with AI1. To do this you can remove the DIRCOR choice from the DIR form: you can simply blank it out on the layout, and you will probably want to remove the corresponding choice in the data statements. If you do this, then the forms DIRCOR, DIRCORP, and DIRCORSEL will no longer be used and you may want to remove them from OAFORM to save space when the form library is compiled. In addition, all of the DO scripts that begin with DIRCOR will not be needed, so if you moved them to the OA$DO directory to include them in your TXL (for better performance) you will probably want to move them back to OA$LIB and recompile the TXL to conserve space. .PARAGRAPH The AI1 implementation required that the key field (called CKEY in my applications) be unique. I do not believe that this is a good choice: for example, if you want to store the numbers for various airlines, you can't have them all with the key AIRLINE: you must have something like AIRLINE1, AIRLINE2, etc. Because my package retrieves on keys starting with the given search criteria, this is not too much of a restriction, but I didn't see any reason for it so I defined a new data file named OA$DATA:CORPHONE.DOM using ANALYZE/FDL and allowed the key to have duplicates. I also did this to use the EDIT/FDL optimize script to look for ways to improve the file structure, and then used CONVERT to load the new file from the old one. I have included the FDL file here, but it is set up for the number of entries we have here, so you may wish to analyze your own file. Though DATATRIEVE doesn't care if duplicates are allowed or not, and my package should work either way, I have not included any double checks against duplicates so if your data file has not been defined to allow duplicate keys and you try to enter a new record with a key that duplicates an existing record, the store will abort with an error. ^&^*NOTE:\*\& the command file CORPHONE.COM references my new data file. If you want to read the AI1 data file you must change OA$DATA:CORPHONE.DOM back to OA$DATA:CORPHONE.DAT before defining the domain (or redefine the domain by hand after invoking CORPHONE.COM). .BLANK 2.TEST PAGE 5.CENTER Bottom line: performance. .PARAGRAPH A good question after all of this is what sort of performance is obtained with the new package, and what did it take to achieve it. To answer the latter question, it took a few hours of work to analyze the old directory file and produce a finished DATATRIEVE record definition that correctly retrieved all of the fields: it would have taken less time to create a new record definition that did not have to correspond exactly with an existing data file. It took another day and a half to create all of the forms and procedures, but this was the first time I had ever used an FMS form with DATATRIEVE: now that I have some experience (and some files of my own to copy) I can bring up a new application in much less time. It took another half a day of concentrated effort to figure out from the ALL-IN-1 manuals how to invoke an application from AI1: again, now that I know how, it takes only a few minutes (plus the time needed to recompile the forms libraries in AI1 for better performance). In short, discounting the time for my learning two new products, the application took very little time to implement. I have probably spent more time writing this document and pushing the package together into a form where I feel I can submit it to the DECUS library and SIG tape than I did developing it in the first place. .PARAGRAPH Performance should be considered in two areas: the time to start the application, and the time to lookup / modify / add / delete entries in the directory. Starting the application from AI1 and a dead start can take 10 to 20 seconds or more, partly from the work needed for AI1 to call up DTR and the time it takes to start (this is slightly faster on subsequent invocations), and partly the time needed to start the procedure MENU__PHONE. Because almost the entire application is contained in one big WHILE loop, DATATRIEVE must parse and "compile" ALL of the statements in all of the procedures before starting. One of the benefits from this is that the application runs quite fast once it is started. Switching from one menu to another is almost as fast as the system can output characters to paint the terminal screen. Within an application such as lookup, once the lookup key is entered retrieving the first entry takes only a second or two, and subsequent retrievals on the same key are about as fast as the screen can be updated. It should be noted, however, that we are running a terrifically overloaded system, and that occasionally when there is a very heavy load the application will stall for a while or be swapped out; when this happens response time slows considerably, but this is not the fault of the application. .BLANK 2.TEST PAGE 5.CENTER Using individual dictionaries (not CORPORATE). .PARAGRAPH In my first implementation, I did not use path names: instead I set the users' default dictionary to to CORPORATE. I did this by having the command .BLANK $ ASSIGN "SYS$USERDEVICE:[ALLIN1]DTRSTART.COM" DTR$STARTUP .BLANK in the A1LOGIN.COM file. When this is done, DATATRIEVE will process any commands in the DTRSTART command file whenever DTR is run. The command file itself contains the following: .BLANK.TEST PAGE 3 SET DICTIONARY CDD$TOP.CORPORATE .BREAK SET PLOTS CDD$TOP.DTR$LIB.PLOTS .BLANK and in the future may invoke any other startup commands I may need for AI1 users invoking DATATRIEVE. .PARAGRAPH An alternative to this is to include a command such as the following in the A1LOGIN.COM file (or the users' individual LOGIN.COM file): .BLANK $ ASSIGN "CDD$TOP.DTR$USERS.CORPORATE" CDD$DEFAULT .BLANK In the absence of any other commands or startup command files, DATATRIEVE will start in the dictionary pointed to by the logical CDD$DEFAULT. Because of the advantages given before for path names, I have dropped this approach but am including here the necessary files, and CORPHONE.OLD which has all of the definitions without path names, to make it easier for anyone who wishes to obtain the various DATATRIEVE definitions without the path names.