Wrench value from your Creo Investments Home Measure the benefits Simple Automation can bring Why
Automate?
A personal way to do PDM Peer to Peer
PDM
3D Drawings Model Based
Design
Smooth out unwanted sharp edges Other
Articles
www.proetoolbox.co.uk - Simple Automation made Simple

Making a Windchill Workspace Content Recovery Tool

From time to time Windchills local cache can get corrupted and 'lose' a users work. Losing your work can result in a lot of rework for obvious reasons. Sometimes the files are actually still on the disk but somehow unlinked from Windchills local database. This article describes building a simple script to parse through a users local cache and allow them to 'rescue' content into a backup directory. This is far less tedious than following the PTC Tech Support procedure.

The first technical point of interest is how Windchill caches store their files. Each locally modified file is prefixed with LID* and has a long number. This means as a user it's not that practical to look in the folder and understand what files actually exist in there.

 

 

The more interesting technical point is that these files are simply Creo files in disguise. If you peek at the file in a text editor, like on the left, you can easily see that the file is a Creo file and if you look really closely you can see the real name inside that file. I'll use that fact to build a simple script to allow us to grab content out of a local cache should the need arise.

 

Application Strategy

  • Loop around all files in the cache.
  • For any files that fall in the users date range proceed.
  • For any files named lid*, read into them and uncover their names .
  • Present the list of files back to the user.
  • User reads list and chooses which files to 'rescue'.
  • Files marked for rescuing, get copied and renamed into a special 'rescue' folder.
  • <html>
    <body>
    <H4>Workspace Content Recovery Tool</H4>
    <TABLE>
    	<TR>
    		<TD>Look ? Days Back: </TD>
    		<TD><INPUT id="Days" type="textbox" value=1 /></TD>
    	</TR>
    	<TR>
    		<TD>&nbsp;</TD>
    		<TD><INPUT type="button" value="List>>>" onclick="ReadFiles()" /></TD>
    	</TR>
    </TABLE>
    <HR>
    <DIV id="UI">
    	<OL>
    		<LI>Set the number of days back to look.</LI>
    		<Li>Press the List>>> Button</Li>
    	</OL>
    </DIV>
    <script>
    //This needs setting to the correct 
    //Creo cache location (i.e. where lid* files are)
    var CacheFolder = "C:\\CreoCache\\";
    
    //This needs setting to a location where
    //you want to rescue files to
    var RescueFolder = "C:\\CacheRescueLocn\\";
    var PotRescueFiles;
    
    //This is the line we match on to extract
    //the Creo filename
    var NameHook = "#- CMNM";
    var NameHookLength = NameHook.length+4;
    
    //Miliseconds timestamp
    var rightNowMS = new Date().getTime() ;
    var fso = new ActiveXObject("Scripting.FileSystemObject");
    
    //Function to parse through the files in the cache
    function ReadFiles()
    {
    	PotRescueFiles = new Array();
    	var Cache = fso.GetFolder(CacheFolder);
    	var CacheFiles = Cache.Files;
    
    	fc = new Enumerator(CacheFiles);
    	var Out = "<TABLE cellpadding=5px>"+
    				"<TR>"+
    					"<TH>LID File</TH>"+
    					"<TH>Real Name</TH>"+
    					"<TH>Date Modified</TH>"+
    					"<TH>Action</TH>"+
    				"</TR>";
    				
    	for (; !fc.atEnd(); fc.moveNext())
    	{
    		var curCacheFileName = fc.item()+"";
    		if (curCacheFileName.indexOf("lid")>-1)
    		{
    			var theFile = fso.GetFile(curCacheFileName);
    			var curFileModDate = theFile.DateLastModified;
    			
    			var msec = Date.parse(curFileModDate);
    			
    			//We're only considering cache files within the 
    			//last ? days
    			var diff = (rightNowMS-msec)/(1000*60*60*24);
    			if (diff>Days.value*1)
    				continue;
    			
    			var curFileName = UncoverRealFileName(curCacheFileName);
    			PotRescueFiles[PotRescueFiles.length] = curCacheFileName+
    									"##"+curFileName;
    
    			var bits = curCacheFileName.split("\\");
    			var curCacheFileName = bits[bits.length-1];
    			
    			var ActionButton = "<INPUT type=\"button\" "+
    						"value=\"Rescue\" onclick=\"RescueFile("+
    						PotRescueFiles.length+")\"></INPUT>";
    			Out+="<TR>"+
    					"<TD>"+curCacheFileName+"</TD>"+
    					"<TD>"+curFileName+"</TD>"+
    					"<TD>"+curFileModDate+"</TD>"+
    					"<TD>"+ActionButton+"</TD>"+
    				"</TR>";
    		}
    	}
    
    	Out += "</TABLE>";
    	UI.innerHTML = Out;
    }
    
    //Function to do the copy of the file
    //and rename on the way
    function RescueFile(idx)
    {
    	var FromTo = PotRescueFiles[idx-1];
    	var bits = FromTo.split("##");
    	var From = bits[0];
    	var To = bits[1];
    	var copiedFile = RescueFolder+"\\"+To;
    
    	var theFile = fso.GetFile(From);
    	theFile.Copy(copiedFile);
    }
    
    //Function to read into the file and uncover the
    //Creo filename from the Hook line
    function UncoverRealFileName(lidFileName)
    {
    	var theFile = fso.OpenTextFile(lidFileName, 1,0);
    	var curFileName = "";
    	while (!theFile.AtEndOfStream)
    	{
    		var textLine = theFile.ReadLine();
    		if (textLine.indexOf(NameHook)>-1)
    		{
    			var tmp = textLine.substring(NameHookLength);
    			var tmp2 = tmp.indexOf(" ");
    			curFileName = tmp.substring(0,tmp2);
    			break;
    		}
    	}
    	theFile.Close();
    	return curFileName;
    }
    
    </script>
    </body>
    </html>
    

     

    ...
    var fso = new ActiveXObject("Scripting.FileSystemObject");
    ...
    var Cache = fso.GetFolder(CacheFolder);
    var CacheFiles = Cache.Files;
    fc = new Enumerator(CacheFiles);
    ...
    for (; !fc.atEnd(); fc.moveNext())
    {
    	var curCacheFileName = fc.item()+"";
    	...
    }
    
    

    Using FileSystemObject to loop around a Folder

    A Folders Files need to be turned into an Enumerator. The syntax feels a like cryptic to me but it's quite easily hackable.

     

    .
    ...
    var fso = new ActiveXObject("Scripting.FileSystemObject");
    ...
    var theFile = fso.GetFile(curCacheFileName);
    var curFileModDate = theFile.DateLastModified;
    var msec = Date.parse(curFileModDate);
    ...
    var diff = (rightNowMS-msec)/(1000*60*60*24);
    if (diff>Days.value*1)
    	continue;
    ...
    

    Using FileSystemObject to get File Properties

    It's interesting to note that there are multiple date properties on files. Last modified is the one that feels most appropriate for our purposes.

    Javascripts built in Date() object can be used to quickly figure out if the data on the file is more than ? days old.

     

    Conclusion

    The FileSystemObject ActiveX control can be very helpful to acheive your Simple Automation needs.