

|
Volume Number: | 9 | |
Issue Number: | 5 | |
Column Tag: | Jörg's Folder |
Related Info: Apple Event Mgr Edition Manager Event Manager
Batch Processing with Apple Events
Solutions for a specific batch task
By Jörg Langowski, MacTech Magazine Regular Contributing Author
Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.
This column was initiated by a reader’s question:
“Dear Jörg,
I am looking for a way to simulate batch files on the mac. I need to run two Fortran programs sequentially, then open Excel and run a macro on the results. What should I use? Can it be done? If you can solve this I'll subscribe!”
Homer Bartlett, Microstar Inc.
Well, we always welcome new subscribers, and your question is actually of very general interest; so this month I’ll write about ‘batch processing with Apple Events’. This is actually a rather ambitious title for what we’ll be doing, sending a couple of Apple Events to other programs to make them do their things; but didn’t the headline catch your eye? So there.
I have found three principal ways to install a way of ‘batch processing’ like Homer Bartlett asked for. a) you write a Fortran program that sends an ‘oapp’ Apple Event to each of the two programs that you want to execute. These two programs leave intermediate results in files. Then, the Fortran program opens Excel (again via an Apple Event), and makes Excel execute a macro file. How this is done, you’ll see soon. b) you write a Fortran program that calls the two other programs via Apple Events as before, but the intermediate results are left in edition files that can be handled by System 7’s Edition Manager. Excel 3.0 and higher supports Subscribe and Publish; if an Excel worksheet is opened that subscribes to those edition files, the data will automatically be updated when the edition is modified. And, last one and easiest one, c) you control everything from Excel - the execution of the two Fortran programs and the following running of the macro. I’ll give you examples for all three of these procedures.
First of all, let’s write two little Fortran programs that calculate our ‘results’ and write them to files. They’re at the start of the example: one calculates a sine curve, the other an exponential, and the Excel macro will later open the files and read them into a worksheet where the results are multiplied and displayed in a graph.
Opening an application from Fortran
Language Systems Fortran provides a rather convenient interface to the four basic required Apple Events. There is a routine F_SendEvent that takes 3 parameters: first a character*4 constant which is the Apple event ID, then a string that specifies the target application and finally an optional document name (also a string). Thus, to open Microsoft Excel from a Fortran program, you might include the line
call F_SendEvent(‘oapp’,’Microsoft Excel’,’’)
and to open the worksheet ‘SinExp’ from Excel
call F_SendEvent(‘odoc’,’Microsoft Excel’,’SinExp’)
These commands are contained in the main program of listing 1. First, in fact, we call the two programs that generate our data, both using the ‘oapp’ event, and then we send the ‘odoc’ event to Excel for opening the macro sheet that opens and multiplies the two output files. Since the program continues after sending the ‘oapp’ event without waiting that the called program has actually written the file, we check whether the output files are actually there before continuing with the macro execution. We assume here that Excel is already open; I did not want to hard code a line that contained the full access path for opening Excel, because that is obviously different from Macintosh to Macintosh. So make sure Excel is open before you run the program.
When the Excel macro sheet has been opened, Excel is ready for executing the macro. Now, how do you make Excel execute a macro - or any command for that matter - from the outside, by sending an Apple event? This is documented in two places: You can either get the ‘Excel 4.0 Software Development Kit’ from Microsoft, or download a file that you can find on AppleLink where the Apple events are described that Excel understands. I downloaded the file, you find it in the archives of the Apple Events discussion in Developer support:System 7 talk. It’s somewhere in April/May 1992; search for ‘Excel’ and you’ll find it; it is a posting by Ben Waldman from Microsoft. The file with the full description of the Apple events understood by Excel is too long to be printed here (45 pages of MS Word text, that would take a full issue of this magazine!). But here, we need only one very basic Apple event, the ‘dosc’ event that is becoming a kind of a standard for remote command execution; for instance HyperCard and the MPW tool server also understand it. The ‘dosc’ event contains a text parameter, which is simply a script of commands to execute by the program; in HyperCard, those are Hypertalk commands and in Excel, any valid Excel command or function.
Language Systems Fortran contains a function that lets you send a script to another program through the ‘dosc’ event. The script lines are defined in a character array, e.g.,
character *40 script(10)
will allow you to write a script of 10 lines of 40 characters maximum each. The function call
Error = F_SendScript('Microsoft Excel',script,10)
would then send the ten lines to the application ‘Microsoft Excel’, which executes them. In our case, we send only one line
'RUN("SinExp!R1C1") '
which is the command to execute the macro starting at row 1, column 1 in the open macro sheet ‘SinExp’. Note that Excel scripts that are sent by Apple events never contain equal signs in front of the command, unlike the same command contained in a worksheet or macro sheet.
So here you go - we have created a Fortran program that calls two other programs and then runs an Excel macro on the results. But there are two other ways to achieve a similar result.
Publish/Subscribe
First of all, one can use the Edition manager, part of System 7. Using this service, a program can ‘publish’ an ‘edition’ of some data, and another program can ‘subscribe’ to it. The publisher and subscriber are then automatically linked together through Apple events so that the data that the subscriber ‘sees’ gets automatically updated when the publisher changes it.
Language Systems Fortran supports the Edition manager. When you open a Fortran file, write data to it, and close it with status=’publish’, you create an edition of that file. Excel 4.0, for instance, can subscribe to such an edition. Doing that from Excel is easy: you select a range of cells and then select ‘Subscribe to ’ from the Edit menu. You can then select an edition file which will be used to update automatically this selected range of cells. In the source code disk, I have already defined a worksheet that contains subscribers to the two data files that are generated by the two Fortran programs.
Listing 2 gives the program example. Here the two ‘data-generating’ parts are implemented as subroutines that can be selected from a menu in the main Fortran program. When you first write the data files, the editions are created. Excel can then subscribe to them, which is done in the worksheet, and a graph is linked to the data. When you then go back to the Fortran program and recalculate the data in the two files with other parameters, you’ll see that the graph also changes (almost) instantaneously.
Calling Fortran programs from Excel macros
This is the last possibility that I found, and it might be the easiest one. There is a function in Excel called EXEC( ) which allows you to execute a program from within Excel. Listing 3 shows a macro which calls the two Fortran programs that create the data files, and then does the calculations and creates a graph like the previous macro. You don’t need to compile a main Fortran program as in the first example, but just execute a sequence of Excel commands which may even be contained in a text file. In fact, this last example might come closest to answer Homer Bartlett’s original question, which was a way to simulate batch files on the Macintosh.
Last remark: I have seen a lot of discussion going on lately about Frontier, a scripting system for the Macintosh developed by UserLand Software. Although I have never seen the product, I’d like to write about it sooner or later when I get hold of it and find the time for writing.
See you next month.
Listing1: Calling other Fortran programs and an Excel macro from Fortran a. The called programs (compile and link using the RunNoWindow script) program doExp real exptab(100),period parameter (period=30.0) do j = 1,100 exptab(j) = exp(-j/period) end do open(10,file='expData') write(10,100) exptab close(10) 100format(x,f10.2) end program doSine real sintab(100),period parameter (twopi = 6.283185,period = 15.0) do j = 1,100 sintab(j) = sin(twopi*j/period) end do open(10,file='SinData') write(10,100) sintab close(10) 100format(x,f10.2) end b. The main program cSends commands to excel via the 'dosc' event. c cCompile with -bkg=4. c program SendScript integer*2 err, F_SendScript external F_SendScript character*30 ch logical exists ch = 'RUN("SinExp!R1C1") ' type *, 'Generating Data ' Error = F_SendEvent('oapp','sinus','') call checkError(Error) Error = F_SendEvent('oapp','expo','') call checkError(Error) exists = .false. do while (.not. exists) inquire(file='expData',exist=exists) end do exists = .false. do while (.not. exists) inquire(file='SinData',exist=exists) end do type *, 'Opening Macro sheet ' Error = F_SendEvent('odoc','Microsoft Excel', * 'SinExp') call checkError(Error) type *, 'Running Macro ' Error = F_SendScript('Microsoft Excel',ch,1) call checkError(Error) type *, 'Quitting Excel ' Error = F_SendEvent('quit','Microsoft Excel','') call checkError(Error) end subroutine checkError(Error) integer*2Error character*10 ch5 if (Error <> 0) then write(ch5,'(i10)') Error call alertbox('Error '//ch5//' occurred.') write(*,*) 'Error '//ch5//' occurred.' stop end if end c. The Excel macro "=OPEN(""expData"")" "=OPEN(""SinData"")" "=SELECT(""R1C2"")" "=FORMULA(""=expData!RC[-1]*RC[-1]"")" "=SELECT(""R1C2:R100C2"")" =FILL.DOWN() "=NEW(2,1)" "=GALLERY.LINE(2,TRUE)" "=ACTIVATE(""SinData"")" =CLOSE(FALSE) "=FILE.DELETE(""SinData"")" "=ACTIVATE(""expData"")" =CLOSE(FALSE) "=FILE.DELETE(""expData"")" =RETURN()
Listing 2: Excel/Fortran interface via Publish/Subscribe program PublishDemo integer*2Error,F_SendEvent external F_SendEvent external doPublish,doExit call moveoutwindow(20,40,300,300) call addmenuitem('Publish','Publish Sine',doSine) call addmenuitem('Publish','Publish Exp',doExp) write(*,*) write(*,*) 'Opening Application Excel ' write(*,*) write(*,*) 'Opening SinExpTable ' Error = F_SendEvent('odoc','Microsoft Excel', * 'SinExpTable') call checkError(Error) write(*,*) write(*,*) 'Opening SinExpChart ' Error = F_SendEvent('odoc','Microsoft Excel', * 'SinExpChart') call checkError(Error) write(*,*) write(*,*) 'Use the Publish menu to update the Excel 1 chart.' call SetExitProc(doExit) end subroutine checkError(Error) integer*2Error character*5ch5 if (Error <> 0) then write(ch5,'(i5)') Error call alertbox('Error '//ch5//' occurred.') write(*,*) 'Error '//ch5//' occurred.' stop end if end !!mp inlines.f subroutine doSine real sintab(100),period parameter (twopi = 6.283185) type *,"Enter period of sine in steps: " accept *,period do j = 1,100 sintab(j) = sin(twopi*j/period) end do open(10,file='SinData') write(10,100) sintab close(10,status='Publish') 100format(x,f10.2) end subroutine doExp real exptab(100),period type *,"Enter decay period in steps: " accept *,period do j = 1,100 exptab(j) = exp(-j/period) end do open(10,file='expData') write(10,100) exptab close(10,status='Publish') 100format(x,f10.2) end subroutine doExit() integer*2Error,F_SendEvent external F_SendEvent Error = F_SendEvent('quit','Microsoft Excel','') call checkError(Error) end
Listing 3: Calling Fortran programs from an Excel macro (this assumes that the Fortran programs are in the same folder as Excel) "=EXEC(""expo"")" "=EXEC(""sinus"")" "=WAIT(NOW()+""00:00:05"")" "=OPEN(""expData"")" "=OPEN(""SinData"")" "=SELECT(""R1C2"")" "=FORMULA(""=expData!RC[-1]*RC[-1]"")" "=SELECT(""R1C2:R100C2"")" =FILL.DOWN() "=NEW(2,1)" "=GALLERY.LINE(2,TRUE)" "=ACTIVATE(""SinData"")" =CLOSE(FALSE) "=FILE.DELETE(""SinData"")" "=ACTIVATE(""expData"")" =CLOSE(FALSE) "=FILE.DELETE(""expData"")" =RETURN()

- SPREAD THE WORD:
- Slashdot
- Digg
- Del.icio.us
- Newsvine