Wednesday, June 4, 2008

File I/O

QUEST SCL has a set of easy to use subroutines for dealing with file input and output. If you've done file I/O in VB or VBA you'll see the syntax is very similar.

File I/O is performed in SCL using file streams. This basically means that you use SCL to open a file and either read it or write to it. To open a file you first need to determine the file stream you want to use. This is just an integer value unique to the file you want to access. If you're planning to open a file stream and close it within a single subroutine the file stream should be an integer between 1 and 15. Anything above that is a global stream, which can be manipulated from any subroutine you're running. This complicates things a bit, and I personally don't use global streams very often. This is because you can pretty easily just append data to a file if you prefer.

There are two ways to pick a stream. You can use a literal integer value and just keep an eye on which you use (in case you decide to manipulate several files at once). Or you can do what I do and use an integer variable so it's easy to change things around, and it makes the code easier to read.

There is a way to ensure you always have a good file stream to use, which is to check file streams starting with one until you find one that's not opened yet. This is facilitated with a while loop and the QUEST built in function is_open.

The is_open function just takes in an integer value and returns true if it's in use as a file stream and false if it's not. Here's the start of a procedure for writing data to a file:

procedure print_data_to_file()
Var
out_stream : Integer
Begin
out_stream = 1
while( is_open( out_stream ) ) do
out_stream = out_stream + 1
endwhile
End


When this procedure gets to the end, out_stream will have a value that is acceptable for use in manipulating file operations. But how do we start file manipulation? With the "open" procedure. This procedure is a little different in that arguments are not passed in parenthesis, but instead the command is built like a sentence. The syntax is:

OPEN FILE name FOR [ BINARY | TEXT ] { OUTPUT | APPEND | INPUT } AS unit_no

In this procedure we can leave out the binary/text declaration, as SCL will default to text. We need to declare how we're opening the file, though. We have the option of: output, append, or input. If we output, then if the file exists it will be wiped clean and started new. If we choose append we have to make sure the file exists to begin with or we'll get an error. If we do append, then any data we write to the file just gets appended to the end of the file. Lastly, if we open for input then we can't make any changes to the file; we're just reading its contents.

From this point on things are pretty straightforward if you're writing out to the file. You simply use the "write" procedure and specify the output stream in order to output to that stream. Here's an example procedure:

procedure print_machine_class_names()
Var
   out_stream : Integer
   check_class : Element_Class
Begin
   out_stream = 1
   while( is_open( out_stream ) ) do
      out_stream = out_stream + 1
   endwhile
   OPEN FILE 'C:/Tmp/machine_names.txt' FOR OUTPUT AS out_stream
   check_eclass = first_element_class
   while( check_eclass <> NULL ) do
      if( check_eclass->type == MACHINE ) then
         write( #out_stream , check_eclass->name , cr )
      endif
      check_eclass = check_eclass->next_eclass
   endwhile
   close #out_stream
End

This is a very straightforward example that first creates/resets the machine_names.txt file, and then proceeds to print out the name of every machine encountered in the model. The last thing it does is close the file stream using the close procedure.

Reading data from a text file is a little less straightforward but not much harder to do. We need to start the same way as writing: find a valid file stream and open the file. Here's the base code:

procedure read_data_from_file()
Var
   in_stream : Integer
Begin
   in_stream = 1
   while( is_open( in_stream ) ) do
      in_stream = in_stream + 1
   endwhile
   OPEN FILE 'C:\Tmp\file_read_example.txt' FOR INPUT AS in_stream
End

Once the file is open, we need to start reading the file. We can do this using the read_line procedure. The syntax is as follows:

READ_LINE ( [ #unit_no,] lvalue )

where lvalue is a string variable we pass in whose value gets assigned as the next line read from the specified file stream. One thing to note here, is that when SCL gets to the end of the file, read_line will return a special predefined string called $EOF. We just keep reading until read_line gives us the $EOF string.

Here is some sample code for reading lines from a file and writing them to the screen.

procedure read_data_from_file()
Var
   in_stream : Integer
   curr_line : String
Begin
   in_stream = 1
   while( is_open( in_stream ) ) do
      in_stream = in_stream + 1
   endwhile
   OPEN FILE 'C:\Tmp\file_read_example.txt' FOR INPUT AS in_stream
   read_line( #in_stream , curr_line )
   while( not curr_line == $EOF ) do
      write( curr_line , cr )
      read_line( #in_stream , curr_line )
   endwhile
   close #in_stream
End

File I/O is a powerful tool for use in QUEST. You can use this to read in a tab-delimited data file with any number of possible uses. For example you can read in process cycle times and assign those times to the specified processes. The possibilities here are really endless, and hopefully this will give you a good idea of how to get started.

1 comment:

RichaerdShu said...

Hi jon
I try to used the OPEN FILE. Put in my model.
Open file Put on Advance/popup/model logic/int.
And Close file put on same path/term.Dec with process logic.The result is file had been open. But the file's content
is blank.May be my process logic on dec is wrong.So could you take a look for me to see what's going
on?
(As attachment had email). And I need data which write to file as blow.
1.AGV1 arrive & left time on Dec point(AGV_Tag11) write to file.
2.AGV1 wating time(=time left-time arrived) on AGV_Tag11 each time.
3.Caculate wating time totally of AGV1 on AGV_Tag11.

REGARDS!