Tcl Examples

Example 1:

When placed into a complete mapping file, this mapping file fragment uses the translation begin/end hooks to cause the translation start and end times to be written to standard output.

FME_BEGIN_TCL set gTranslationStartTime [clock format [clock seconds]]

FME_END_TCL puts \”Translation started time: $gTranslationStartTime  \nTranslation end time: [clock format [clock seconds]]\”

Example 2:

When placed into a complete mapping file, this mapping file fragment uses the translation end hook and the global FME_Status variable to output a translation success or failure message.

Notice that it first defines a procedure which will do this testing and output, and then it actually calls the procedure to do the work.

FME_END_TCL proc finally {} {                      \
       global FME_Status;                          \
       if {$FME_Status == "1"} {                   \
          puts \"Translation was successful\";     \
       } else {                                    \
          puts \"Translation was NOT successful\"; \
       };                                          \
    };                                             \
    finally

This same example could also be written without using a procedure:

FME_END_TCL if {$FME_Status == "1"} {                        \
                    puts \"Translation was successful\";     \
                 } else {                                    \
                    puts \"Translation was NOT successful\"; \
                 }

Example 3:

This example shows how macros used by the mapping file can be passed into a beginning or ending script as an argument to a procedure. This could be used to move, copy, e-mail, backup or otherwise post/pre-process the destination datasets.  In this example, the destination dataset is backed up prior to the start of translation to save any existing output file that would normally be overwritten.

The mapping file fragment is:

# The macro being used in this mapping file to set the destination 
# dataset directory is DestDataset.  
DWG_DATASET "$(DestDataset)"
# Source in the Tcl script to run at the conclusion of this
# translation.  The script is stored in the same directory as
# this mapping file.
FME_BEGIN_TCL source {$(FME_MF_DIR_UNIX)/backup.tcl}; backup {$(DestDataset)}

The contents of the “backup.tcl” file are:

 proc backup {filename} {
    if {[file exists $filename]} {
        file copy -force $filename $filename.bak
    }
}

Example 4:

This example shows how the FME_END_TCL could be used to chain together translations, based on the results of a particular translation. In this example, if the original translation fails, then another fme session will be initiated.

Note that in the Tcl file, the 2> NUL: routes any log and error messages to the null device, which effectively causes them to be ignored.

The mapping file fragment is:

# Source in the Tcl script to run at the conclusion of this
# translation.  The script is stored in the same directory as
# this mapping file.
FME_END_TCL source {$(FME_MF_DIR_UNIX)/tryAnother.tcl}

The contents of the “tryAnother.tcl” file are:

set outputFile [open c:/temp/status.txt w+] 
if { $FME_Status == "1" } {
   puts $outputFile "Translation was successful"
} else {
   puts $outputFile "Translation failed -- running alternate translation"
   exec fme.exe alternateTranslation.fme 2> NUL:
}
close $outputFile

Example 5:

This example uses an external script to output a complete set of translation statistics to a summary file at the end of translation.

The mapping file fragment is:

# Set a mapping file id so that the Tcl finalization procedure "knows" which 
# mapping file was being run
MAPPING_FILE_ID Shape to AutoCAD
# Log all the message numbers so that we can later pull out only those
# we are interested in.
LOG_MESSAGE_NUMBERS yes
# Source in the Tcl script to run at the conclusion of this
# translation.  The script is stored in the same directory as
# this mapping file.
FME_END_TCL source {$(FME_MF_DIR_UNIX)/tclFinalization.tcl}

The contents of the “tclFinalization.tcl” file are:

# Open a file for writing out the translation stats
set outputFile [open c:/temp/stats_out.txt w+]
# Check for the translation status and output the desired message
if { $FME_Status == "1" } {
   puts $outputFile "Translation was successful"
} else {
   puts $outputFile "Translation failed"
   puts $outputFile "Error message was: $FME_FailureMessage"
};
puts $outputFile ""
# Output the unique Mapping file identifier set in the mapping file using MAPPING_FILE_ID
puts $outputFile "Translation summary for $FME_MappingFileId"
# Output the number of features read\written per feature type.
puts $outputFile "---------------------------------------------------------------------------"
puts $outputFile "                         Features read summary                                 "
puts $outputFile "---------------------------------------------------------------------------"
# Loop through a sorted listed of the feature types that were read, and output the
# count for each one
set formatSpec "%-65s: %12s"
foreach featType [lsort [array names FME_FeaturesRead]] {
    puts $outputFile [format $formatSpec $featType $FME_FeaturesRead($featType)]
}
puts $outputFile "---------------------------------------------------------------------------"
puts $outputFile [format $formatSpec "Total Features Read" $FME_TotalFeaturesRead];
puts $outputFile "---------------------------------------------------------------------------"   
puts $outputFile ""
puts $outputFile "---------------------------------------------------------------------------"
puts $outputFile "                         Features Written summary                              "
puts $outputFile "---------------------------------------------------------------------------"
# Loop through a sorted listed of the feature types that were written, and output the
# count for each one
foreach featType [lsort [array names FME_FeaturesWritten]] {
    puts $outputFile [format $formatSpec $featType $FME_FeaturesWritten($featType)]
}
puts $outputFile ""
puts $outputFile "---------------------------------------------------------------------------"
puts $outputFile [format $formatSpec "Total Features Written" $FME_TotalFeaturesWritten]
puts $outputFile [format $formatSpec "Total Coordinates Written" $FME_TotalCoordinates]
puts $outputFile "---------------------------------------------------------------------------"
puts $outputFile ""
# Look for any lines in the logfile that were warnings, and output a count of them to
# the summary file.  Also, check if there was any "unexpected input remover"
# statistics line and report a non-zero count if there was (this may happen
# when workbench generated mapping files are run against input datasets
# with different feature types than those that were expected)
# And also fish out the system status log message (which is #246014) and copy
# it into the output file
set logFileExists [file exists $FME_LogFileName]
set warnings 0
if {$logFileExists} {
   set logFile [open $FME_LogFileName r]
   while {[gets $logFile line] >= 0} {
      if {[regexp {WARN} $line]} {
         incr warnings
      } elseif {[regexp {#246014} $line]} {
        puts $outputFile "-------------------------------------------------------------------"
        puts $outputFile $line
        puts $outputFile "-------------------------------------------------------------------"
        puts $outputFile ""
      } elseif {[regexp {Unexpected Input Remover} $line]} {
          set totalFeatures 0
          set acceptedFeatures 0
          set rejectedFeatures 0
          set line [regsub {^.*Unexpected} $line {Unexpected}]
          catch {scan $line "Unexpected Input Remover(TestFactory): Tested %d input features -- %d features passed, %d features failed." totalFeatures acceptedFeatures rejectedFeatures}
          if {$rejectedFeatures > 0} {
            puts $outputFile "---------------------------------------------------------------"
            puts $outputFile [format $formatSpec "Features with Unexpected Feature Types" $rejectedFeatures]
            puts $outputFile "---------------------------------------------------------------"
            puts $outputFile ""
          }
      }
   }
   close $logFile
}
puts $outputFile "---------------------------------------------------------------------------"
puts $outputFile [format $formatSpec "Logfile $FME_LogFileName Warnings" $warnings]
puts $outputFile "---------------------------------------------------------------------------"
puts $outputFile ""
puts $outputFile "---------------------------------------------------------------------------"
puts $outputFile [format $formatSpec "Total Elapsed Time (seconds)" $FME_ElapsedTime]
puts $outputFile [format $formatSpec "Total CPU Time (seconds)" $FME_CPUTime]
puts $outputFile "---------------------------------------------------------------------------"
puts $outputFile ""
# And close the file
close $outputFile

Example 6: Installing TclODBC

This example uses an external script to insert a record of the translation’s activity into a database using the TclODBC package. In this example, an Access database is created (if necessary) and populated with a row for each translation that completes.

To use TclODBC within the FME environment, follow these steps:

Read about the TclODBC package at http://wiki.tcl.tk/2151

Download the TclODBC package from http://sourceforge.net/projects/tclodbc

Unzip the result

Edit the supplied setup.tcl to change:

       if [string match *lib $i] { 
to
       if [string match *lib* $i] {

In a command prompt in the folder holding setup.tcl, run “fme setup.tcl”. This will install the TclODBC package in the FME’s Tcl environment.

Ensure any scripts that require the TclODBC package start with:

      package require tclodbc

 To invoke the sample script below which does the database insertion, the mapping file contains this line:

 FME_END_TCL source {$(FME_MF_DIR_UNIX)/recordTranslationODBC.tcl}

 The contents of the “recordTranslationODBC.tcl” file are:
#=====================================================================
#
# recordTranslationODBC.tcl
#
# This script records the execution of a translation using
# the TclODBC package.  This example includes the creation of the
# Access database and table for storing the translation results.
# One row for each translation run is inserted into the table.
# Note that for an acutal production system the "puts" statements
# would be removed.
package require tclodbc
#=====================================================================
#
# Set up some variables that are used within to create and connect
# to the database via ODBC
set driver "Microsoft Access Driver (*.mdb)"
set dbfile c:/translations.mdb
set dsn XLATION
#=====================================================================
# Create the database if it isn't already there. We wouldn't do this if the
# database was a server-based one, but for this example we're just using Access.
if {![file exists $dbfile]} {
    puts "Creating database $dbfile"
    database configure config_dsn $driver [list "CREATE_DB=\"$dbfile\" General"]
} else {
    puts "Using existing database $dbfile"
}
#=====================================================================
# Make an ODBC datasource for this database, and connect to it
# First always remove the DSN if it was there already.  If we were
# using ODBC to connect to a "real" database, then we'd just assume
# the DSN is already valid
catch {database configure remove_dsn $driver "DSN=$dsn"}
database configure add_dsn $driver [list "DSN=$dsn" "DBQ=$dbfile"]
database db $dsn
#=====================================================================
# Create the table we want to insert into if it wasn't there already
if {[llength [db tables XLATION_RESULTS]] == 0} {
  puts "Creating XLATION_RESULTS table in database $dbfile"
  db "CREATE TABLE XLATION_RESULTS (
      MappingFileID VARCHAR(50),
      StartTime TIMESTAMP,
      EndTime TIMESTAMP,
      CpuTime DOUBLE,
      Successful CHAR(3),
      NumFeatures INTEGER)"
} else {
  puts "XLATION_RESULTS table present in database $dbfile"
}
#=====================================================================
# All of that was just setup, now we actually can insert our row,
# commit it, and disconnect from the datasource
set Success yes
if {$FME_Status == 0} {set Success no}
db "INSERT INTO XLATION_RESULTS
       (MappingFileID, StartTime, EndTime, CpuTime, Successful, NumFeatures)
       VALUES ('$FME_MappingFileId', \{ts '$FME_StartingTimeStamp'\},
               \{ts '$FME_EndingTimeStamp'\},
               $FME_CPUTime, '$Success', $FME_TotalFeaturesWritten)"
       
db commit
db disconnect
#=====================================================================
# If you were connecting to a server-based database, you probably
# would NOT remove the ODBC DSN at the end. But since we were file based
# we will remove it
database configure remove_dsn $driver "DSN=$dsn"

Example 7: Installing Oratcl

This example uses an external script to insert a record of the translation’s activity into an Oracle database using the Oratcl package.

To use Oratcl within the FME environment, follow these steps:

Install and configure Oracle client software on your computer. Verify you can access your database using sqlPlus.

Read about and download the Oratcl package at http://oratcl.sourceforge.net.

Unzip the result into $FME_HOME/tcl_library/oratcl4.1 (you will have to create this folder, and the 4.1 may need to change to match version of Oratcl you’ve downloaded). After unzipping, move the msvcr70.dll and oratcl41.dll to $FME_HOME/tcl_library (up one level).

Ensure any scripts which require the Oratcl package start with:

      package require Oratcl

(It is important that the “O” in Oratcl is uppercase.)

To invoke the sample script below script which does the database insertion, the mapping file contains this line:

FME_END_TCL source {$(FME_MF_DIR_UNIX)/recordTranslationOracle.tcl}

The contents of the "recordTranslationOracle.tcl" file are:

#=====================================================================
#
# recordTranslationOracle.tcl
#
# This script records the execution of a translation using
# the Oratcl package.  This example includes the creation of the
# XLATION_RESULTS table for storing the translation results.
# One row for each translation run is inserted into the table.
package require Oratcl
#=====================================================================
# Login to the Oracle service
set username scott
set password tiger
set service  amidala
set loginHandle [oralogon $username/$password@$service]
#=====================================================================
# Determine if the xlation_results table we wish to record results to exists
set tableExists no
set statementHandle [oraopen $loginHandle]
orasql $statementHandle "select * from user_tables where table_name = 'XLATION_RESULTS'"
while {[orafetch $statementHandle -datavariable row] == 0} {
     set tableExists yes
}       
#=====================================================================
# Create the xlation_results table if we need to
if {$tableExists == "no"} {
     orasql $statementHandle "CREATE TABLE XLATION_RESULTS (
                                 MappingFileID VARCHAR(50),
                                 StartTime TIMESTAMP,
                                 EndTime TIMESTAMP,
                                 CpuTime FLOAT,
                                 XlationSuccessful CHAR(3),
                                 NumFeatures INTEGER)"
}
#=====================================================================
# Insert the translation results into the table
set Success yes
if {$FME_Status == 0} {set Success no}
orasql $statementHandle "INSERT INTO XLATION_RESULTS
              (MappingFileID, StartTime, EndTime,
               CpuTime, XlationSuccessful, NumFeatures)
              VALUES ('$FME_MappingFileId',
                      timestamp '$FME_StartingTimeStamp',
                      timestamp '$FME_EndingTimeStamp',
                      $FME_CPUTime, '$Success',
                      $FME_TotalFeaturesWritten)" -commit
#=====================================================================
# Shut down the statement handle
oraclose $statementHandle
#=====================================================================
# Logout of our oracle service
oralogoff $loginHandle