#***************************************************************************** #This perl script is used to parse ISA headers for EDI Transactions #The transactions can then be forwarded to appropriate receivers by FTP #or other means. In effect it is an EDI store and forward system. #diskcrash Jan 24, 2002 #***************************************************************************** #Change History # #date #Description #who did it #01/28/02 V1.0 Creation diskcrash #02/06/02 V1.1 Added choice of sender or receiver for output diskcrash #02/06/02 V1.2 Removed additional end of transaction LF chars diskcrash # # #***************************************************************************** #***definitions my(@inlines); my(@lines); my($line); my($slash)="/"; #**** Now set which directories are created, for senders vs. receivers #**** Generally the directories created are receivers of wxyz data my($nameflag)="r"; #file set for receiver output #*** now set the default device for the file system $defdev="c:"; #*** now establish the default directories $excpdir=$defdev."/wxyzprod/wxyzexcp/"; #dir to put exception files in $logfile=$defdev."/wxyzprod/wxyzlog.txt"; #log file $indir=$defdev."/wxyzprod/wxyzin/"; #input directory $outdir=$defdev."/wxyzprod/suppliers/"; #top of suppliers dir tree $archive=$defdev."/wxyzprod/wxyzarch/"; #original files go here after processing #*********************************************** undef $/; #this removes the default \n input line separator #so that files shall be read #in as one giant string, regardless of embedded #line controls. #Note that this radically impacts all file reads #vs. normal "read by record" behavior. #*********************************************** #**************************************************************************** #Start this perl script on system boot #It first writes a timestamp to the wxyzlog.txt file in /wxyzprod #In this startup section we'll check to make sure the script can read from #the /wxyzprod/wxyzin directory, and stop if it can't #The script also tests to see if the logfile is larger than 100 megabytes #if so it needs looking at or deleting. The logfile will be recreated after #deletion #**************************************************************************** #******write start time to logfile open( LOGGER, ">>$logfile"); ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydat,$isdst)=localtime(); print LOGGER $hour , $min ,$sec , $mday , $mon , $year," wxyzprod started\n"; close (LOGGER); #*******check for input directory and exception #******* if (!opendir(PRODDIR,"$indir")) { ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydat,$isdst)=localtime(); $timestamp= $hour . $min .$sec . $mday . $mon . $year; open( LOGGER, ">>$logfile"); print LOGGER $timestamp . "\wxyzprod\wxyzin directory not found-stopped!\n"; close(LOGGER); die("No wxyzprod/wxyz input directory found"); } #******check for logfile size and e-mail #******if the log file got really huge, it should be at least inspected ($device,$inode,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime, $blksize,$blocks) = stat(LOGGER); if ($size > 100000000) # if log file size over 100 meg stop! { ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydat,$isdst)=localtime(); $timestamp= $hour . $min .$sec . $mday . $mon . $year; open( LOGGER, ">>$logfile"); print LOGGER $timestamp . "log file greater than 100Meg - WARNING!\n"; close(LOGGER); } #**************************************************************************** #The intro stuff is done, the main loop follows. It sleeps for 20 seconds #then checks for new files in the /wxyzprod/wxyzin directory. #If it finds one or more, it gets the file list and then checks the following: # Is the file at least 10 seconds old since last modified, if not # it throws it back to get processed 20 seconds later # Does the file have a string ISA in it? If not it copies it to the # wxyzprod/wxyzexcp directory, might have been a CAD file or something. # It wasn't an EDI file, and there is no ISA header to decode it. # Does the file have a string IEA in it? (Same story, see above) # If the file meets the above criteria, then its scanned for embedded Line #Feeds - as in ASCII "0A". These are replaced by nulls. There is a single #trailing LF. #It is assumed there could be an unlimited number of ISA/IEA header #pairs. The file is scanned for each ISA and - Lordy Yes- fixed byte #count offsets are read in to reach the sender, receiver, date and time. #This practice of fixed offsets could, of course, be blown out of the water #with a single change. Fortunately the ISA header structure #has been relatively fixed and stable over the years. #Just be aware of this potential issue. <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< #**************************************************************************** #***** main loop - the other end is at the bottom of the script A: #*****sleep 20 seconds, then check for files again sleep(20); #set deadman variable to 1 #stubbed out #*********************************************************************** #determine if any files exist in wxyzprod\wxyzin #open the directory and get file list, then read files one by one #from the file list #************************************************************************ opendir (FDIR,"$indir"); @filelist=readdir(FDIR); close(FDIR); #*********************************************************** # Now the first two "files" will be . and .. which we will skip #********************************************************** #************************************************************** #So.. if the file list is null, the following loop is bypassed # # This next loop reads each filename from @filelist #****************************************************************** foreach $infile (@filelist) #*** This is the start of the file processing #loop, which ends after the last file is read { if ($infile eq "." || $infile eq "..") #vestigial returned files-ignore { goto escape1; } #****************************************************************** #we want to make sure the file is finished being written, so.. #determine if $mtime > 10 seconds and skip if not #If its too young we'll get back to it next time #****************************************************************** $getinfile=$indir.$infile; ($device,$inode,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime, $blksize,$blocks) = stat($getinfile); my($timenow)=time; $delta=$timenow-$mtime; if ($delta < 10) #***file too young, wait a few seconds { next; #***jump to the end of the loop } #***ok, its older than ten seconds #***so read the whole file into an array at one time open(INF,"<$getinfile"); #***open each file in turn #************************************************************* $line=; #This reads the whole input file at one time into the $line. #it will usually have only one entry (a single record) #, but if it has more and it is an EDI file, it will will need #all of the embedded LFs replaced by nulls. #This process has been tested for file sizes up to 50 Mbytes #on a 128 Mbyte Win2K system. I wouldn't really push this #for files above 5 Mbytes, but then most EDI files are under # 10K bytes #************************************************************* close(INF); #***close the input file #*****determine if ISA header exists and if not post a log event and toss #the file to the /wxyzproc/wxyzexcp exception directory if (index($line,"ISA") == -1) { #if we are here,there is no ISA header # so its not an EDI file #write to the exception area and #delete from wxyzin dir #********************* ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydat,$isdst)=localtime(); $timestamp= $hour . $min .$sec . $mday . $mon . $year; open(LOGGER,">>$logfile"); print LOGGER "$timestamp , $infile , no ISA header found \n"; close(LOGGER); $outfile=$excpdir . $infile; open(IEXCP,">$outfile"); print IEXCP "$line"; close(IEXCP); #****now delete the original file from the input directory unlink("$getinfile"); goto escape1; #exit from the file reading loop and look for next file } #********determine if IEA trailer exists and exception if (index($line,"IEA") == -1) { #if you are here,it has no IEA trailer # not an EDI file #write to the exception area and #delete from wxyzin dir #******************* ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydat,$isdst)=localtime(); $timestamp= $hour . $min .$sec . $mday . $mon . $year; open(LOGGER,">>$logfile"); print LOGGER "$timestamp . $infile . -no IEA trailer found\n"; close(LOGGER); $outfile=$excpdir . $infile; open(IEXCP,">$outfile"); print IEXCP "$line"; close(IEXCP); #****now delete the original file from the input directory unlink("$getinfile"); goto escape1; #exit from the file reading loop and look for next file } $origline=$line ; #***keep a copy of the original line, before LF removal #This string will be written to the wxyz Arcive, as is #***************************************************************************** #strip embedded LFs $line=~ s/\x0a//; #this replaces line feeds with nulls, in the whole #file #*** (STUBBED OUT FOR NOW now put the trailing LF back in #$line=$line.\x0a; # the file is now built to spec - January,2002 #********************** #************************************************************************** #In this next loop we will cycle through the file looking for the start of #ISA headers. Each header represents a transaction, possibly from a unique #sender. Each one is parsed out and the transaction is written with a unique #file name to the /wxyzprod/supplier/"unique" directory. $ipos=0; #set initial scanning point in $line, in the file #***find next ISA - This is the start of the transaction loop, within a file while (index($line,"ISA",$ipos) > -1) #if false we have processed all transactions #in the file, so skip forward to next file { $ipos=index($line,"ISA",$ipos); #Use this position to get next variables. #***sender is 35 after ISA #***get sender $sender=substr($line,$ipos+35,5); #***get receiver $receiver=substr($line,$ipos+54, 5); #*** based on the $nameflag being r or s, use the receiver or sender as the key #***name to used to make target directories if ($nameflag eq "r") { $fname=$receiver; } elsif ($nameflag eq "s") { $fname=$sender; } #***get date $edidate=substr($line,$ipos+70, 6); #***get time $editime=substr($line,$ipos+77, 4); #***now check for the IEA position, near the end of the transaction $ieapos=index($line,"IEA",$ipos+80); #always at least 80 chars away $transend=$ieapos+14; #14 chars after the IEA it ends $tlength=$transend-$ipos+1; #get length of the transaction #**** Now, get just this transaction as a substring from the $line string $transaction=substr($line,$ipos,$tlength); $ipos=$transend; #set up for next ISA scan #as the start point for the next #transaction (if there is one) #***the following file counter is used if there are ambiguous file names $fcounter=1; $outfile="EDI_".$sender.$receiver.$edidate.$editime.$fcounter.".txt"; #***check for sender or receiver directory as selected by $nameflag if (!opendir(DIR,$outdir.$fname)) { mkdir($outdir.$fname,0777); ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydat,$isdst)=localtime(); $timestamp= $hour . $min .$sec . $mday . $mon . $year; open(LOGGER,">>$logfile"); print LOGGER "$timestamp . $infile . new dir made for $fname\n"; close(LOGGER); } close(DIR); #***Check for unique file name and keep going until you get one while (-e $outdir.$fname.$slash.$outfile) { $fcounter=$fcounter+1; $outfile="EDI_".$sender.$receiver.$edidate.$editime.$fcounter.".txt"; } #***ok, there is now a unique file name #***write new file to dir and filename, close it $supname=$outdir . $fname . $slash . $outfile; open (FOUT, ">$supname"); print FOUT "$transaction"; close(FOUT); } #******this is the end of the transaction loop, within a file #******************************************************************** #so now we write the original data to the archive and make a note #in the log file #**write create date/time, process date/time, file size and trans count to log ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydat,$isdst)=localtime(); $timestamp= $hour . $min .$sec . $mday . $mon . $year; open(LOGGER,">>$logfile"); $flen=length($origline); $outbuf=$timestamp ." " . $infile ." sender ".$sender." Receiver ".$receiver ." length ". $flen; print LOGGER "$outbuf\n"; close(LOGGER); #***write the original file into the archives, with original file name $archname=$archive . $infile; open(ARCH, ">$archname"); print ARCH "$origline\n"; close(ARCH); #****now delete the original file from the /wxyzprod/wxyzin directory unlink("$getinfile"); escape1: #if file name is . or .. we got here and bypassed processsing } #***** This is the end of the file processing loop, all files #in the wxyzprod/wxyzin directory have been read and processed at this #point - so go back and sleep for a few seconds, then look for files #again #*****back to the top goto A; #**************************************************************************** #This is the end of the wxyzprod perl script #*************************************************************************** #The Deadman process - stubbed out for now #Every 20 seconds the wxyzprod sets a variable to 1 #Deadman runs every two minutes and sets it to zero #If deadman detects two zeros in a row, send e-mail and log file it