ImportSAT using Python

I have set of code to convert .sat file to .rfa file using dynamo and python but the python script is returning null list. Can anyone help on this

import System
import sys
import clr

clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
from System.Collections.Generic import *

clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB import Transaction

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.Elements)

clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

clr.AddReference('DSCoreNodes')
from DSCore import *

# Get doc, uiapp and app
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application

# input .rfa file paths
RFAfilepaths = UnwrapElement(IN[0])
# Input .sat file paths
SATfilepaths = UnwrapElement(IN[1])
SATfilepaths_name = [i[:-4] for i in SATfilepaths]

# SAT Import options
SAT_IOption = SATImportOptions()
SAT_IOption.VisibleLayersOnly = True
SAT_IOption.Placement = ImportPlacement.Origin
SAT_IOption.ColorMode = ImportColorMode.Preserved
SAT_IOption.Unit = ImportUnit.Default
# Save as options
Save_Option = SaveAsOptions()
Save_Option.OverwriteExistingFile = True
Save_Option.Compact = True
Save_Option.MaximumBackups = 1

TransactionManager.Instance.ForceCloseTransaction()

# list of updated .rfa files
updated = []

for RFAfilepath in RFAfilepaths:
	RFAfilepath_name = RFAfilepath[:-4]
	if RFAfilepath_name in SATfilepaths_name:
		# Find corresponding SAT filepath
		SATfilepath = SATfilepaths[SATfilepaths_name.index(RFAfilepath_name)]

		# Open .rfa file
		RFAopen = app.OpenDocumentFile(RFAfilepath)

		# Start transaction
		trans = Transaction(RFAopen, RFAfilepath_name)
		trans.Start()

		# Get all elements from open .rfa document
		collector = FilteredElementCollector(RFAopen)

		# delete existing elements
		allElements = collector.OfCategory(BuiltInCategory.OST_GenericModel).ToElements()
		for i in allElements:
			RFAopen.Delete(i.Id)

		# Import .sat file
		#RFAopen.Import(SATfilepath, SAT_IOption, view)
		shapeImporter = ShapeImporter()
		shapeImporter.InputFormat = ShapeImporterSourceFormat.SAT
		shapes = shapeImporter.Convert(RFAopen, SATfilepath)
		dsImportedSat = DirectShape.CreateElement(RFAopen, ElementId(BuiltInCategory.OST_GenericModel))
		dsImportedSat.SetShape(shapes)

		# Commit transaction changes
		trans.Commit()
		trans.Dispose()

		# Save .rfa file
		RFAopen.SaveAs(RFAfilepath, Save_Option)
		RFAopen.Close(False)
		updated.append(RFAfilepath)

#Save resulting Revit file to the destination path:
OUT = updated;

@GavinCrump , I have created one newly. Can you help me on this

SAT is beyond what I delve into, but hopefully others on the forum can.

Are you sure the if statement is returning any value?

Best to start debugging this by appending all the variables to the output value as a list.

1 Like

Hi @jacob.small, thanks for the comments. I tried in the same way and found that if condition was not satisfied later I modified the RFA filepath to be same of SAT filepath.
Now I’m getting error on below line stating rfa file doesn’t exists. I want to create empty rfa file with the given name is it possible?

Yes. This is the API call, though you may want to look into copying a single RFA a few times on disc that has all your configurations and resources completely setup in it rather than using a template (or convert the RFA to RFT).

If you want a sample of how it is utilized, look into the springs package. That said, for what you are doing, use the springs package or the FamilyType.ByGeometry node which is out of the box - no need to reinvent the wheel here. Once you have the families in the document you can save them out.

Hi @jacob.small , I have modified the codes and trying to do the same operation, but I’m getting below warning. Can you help to identify what is causing the error. I tried manually loading all the .sat files using ImportCAD icon in Revit and everything worked properly.

image

import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

clr.AddReference('RevitAPI')
clr.AddReference('RevitAPIUI')
import Autodesk
from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import *

clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

import os

uiapp = Autodesk.Revit.UI.ExternalCommandData.Application
doc = DocumentManager.Instance.CurrentDBDocument
TransactionManager.Instance.EnsureInTransaction(doc)

def sat_to_rfa(sat_file_path, rfa_file_path, rfa_file_name):
    """Convert a SAT file to a RFA file"""
    
    # Create the document and the application
    app = Autodesk.Revit.ApplicationServices.Application.Create
    familyDoc = doc.Application.NewFamilyDocument(r"C:/ProgramData/Autodesk/RVT 2021/Family Templates/English-Imperial/Specialty Equipment.rft")
    options = SATImportOptions()
    options.Placement = ImportPlacement.Origin
    options.Unit = ImportUnit.Foot
    doc.Import(sat_file_path, options,familyDoc)
    # Save the family document
    doc.SaveAs(rfa_file_name)
    # Close the family document
    doc.Close()     
    

# Set the paths for the input SAT files and output RFA files
sat_folder_path = r'C:\Users\vramyaa\Revit\input'
rfa_folder_path = r'C:\Users\vramyaa\Revit\output'
sat_files = os.listdir(sat_folder_path)
rfa_files = os.listdir(rfa_folder_path)
# Loop through all the SAT files in the folder
for sat_file_name in sat_files:
    if sat_file_name.endswith('.sat'):
        sat_file_path = os.path.join(sat_folder_path, sat_file_name)
        
        # Generate the corresponding RFA file path
        rfa_file_name = sat_file_name[:-4] + '.rfa'
        rfa_file_path = os.path.join(rfa_folder_path, rfa_file_name)
        
        # Convert the SAT file to the RFA file
        sat_to_rfa(sat_file_path, rfa_file_path, rfa_file_name)
        
OUT = sat_files,rfa_files,sat_folder_path, rfa_folder_path, rfa_file_name, rfa_file_path

I tried to shapeimpoter instead of SATImportOptions() and I’m getting this error.

Also .sat works are loaded into the single family which is opened in the background

You’ll need to manage your transactions, first full closing all of the transactions, then starting a new one, then doing the import in the family, then commit that transaction, then load the family into the project (assuming that was a goal as well).

Note that you should only be using th SAT import options, not the DWG ones as you don’t have a DWG; CAD in this instance of the Revit user interface doesn’t mean AutoCAD, but any one of many computer aided drawing formats.

@jacob.small, thanks for your guidance I have updated the code to commit and start the particular process but I’m getting error with file saving functionality. It throws warning that the file already exists however the output folder is empty
image

import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

clr.AddReference('RevitAPI')
clr.AddReference('RevitAPIUI')
import Autodesk
from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import *

clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

import os

uiapp = Autodesk.Revit.UI.ExternalCommandData.Application
doc = DocumentManager.Instance.CurrentDBDocument
TransactionManager.Instance.EnsureInTransaction(doc)

Save_Option = SaveAsOptions()
Save_Option.OverwriteExistingFile = True
Save_Option.Compact = True
Save_Option.MaximumBackups = 3

updated = []

def sat_to_rfa(sat_file_path, rfa_file_path, rfa_file_name):
    """Convert a SAT file to a RFA file"""
    
    # Create the document and the application
    app = Autodesk.Revit.ApplicationServices.Application.Create
    familyDoc = doc.Application.NewFamilyDocument(r"C:/ProgramData/Autodesk/RVT 2021/Family Templates/English-Imperial/Specialty Equipment.rft")
    trans = Transaction(familyDoc, "test");
    trans.Start()
    shapeImporter = ShapeImporter()
    shapeImporter.InputFormat = ShapeImporterSourceFormat.SAT
    shapes = shapeImporter.Convert(familyDoc, sat_file_path)
    trans.Commit()
    trans.Dispose()
    # Save the family document
    familyDoc.SaveAs(rfa_file_path)
    #close the family document
    familyDoc.Close(True)  
          
    

# Set the paths for the input SAT files and output RFA files
sat_folder_path = r'C:\Users\vramyaa\Revit\input'
rfa_folder_path = r'C:\Users\vramyaa\Revit\output'
sat_files = os.listdir(sat_folder_path)
rfa_files = os.listdir(rfa_folder_path)
# Loop through all the SAT files in the folder
for sat_file_name in sat_files:
    if sat_file_name.endswith('.sat'):
        sat_file_path = os.path.join(sat_folder_path, sat_file_name)
        
        # Generate the corresponding RFA file path
        rfa_file_name = sat_file_name[:-4] + '.rfa'
        rfa_file_path = os.path.join(rfa_folder_path, rfa_file_name)
        updated.append(rfa_file_path)
        # Convert the SAT file to the RFA file
        sat_to_rfa(sat_file_path, rfa_file_path, rfa_file_name)
        
OUT = sat_files,rfa_files,sat_folder_path, rfa_folder_path, updated

If my line counts are right and I am understanding your code’s intent, this error is at the save as action of the definition where you are attempting to save the family over the family you are already in. That is something that Revit prevents to keep you from corrupting data if memory serves.

Perhaps a save method would be a better fit?

Hi @jacob.small , I changed the SaveAs funtionality to Save but it throws another error

Read the message carefully - it’s asking for a SaveOptions object which you’ve had similar objects in the form of a SaveAsOptions elsewhere in the code. You are providing a string, specifically the path you want the document to be saved to. The good news is we can simplify this.

Rather than giving an answer, I’m going to try a bit of teaching you how to explore effectively on your own, as you’re SUPER close and this is a really useful skill.

Go to RevitAPIdocs.com, set the version at the top to the version you’re authoring in, and search for the “Save” and hit return. You should have something like 152 results which is a lot. Significantly more than I’d want to look over anyway, so let’s try a new search.

The main function types in the Revit API are classified as methods (actions), constructors (make new stuff), and properties (information about an object). Saving a document isn’t making a new document, nor is it getting or setting information about an object, so let’s assume it’s a method for now. Searching “save method” does in fact give better results - 53 in total, and the top hit looks promising as it’s a method in the Autodesk.Revit.DB.Document class, and we want to save a document.

Clicking that to see what we can learn about it, you’ll notice that on the left we have been ported to the save method of the document class, with all other methods of the class also expanded. There are also two save methods shown in this panel under the “Save Method” tree. "Save Method, and “Save Method (SaveOptions)”. The later sounds familiar to the error we got before, so you could look into that, but the version without save options is interesting as well, as it indicates there is no additional information provided - we can just open and close the parenthesis with nothing internal to it.

Be sure to read the remarks and exceptions here, as there are a few more “gotcha” moments which may trip you up.

Give it a shot and let us know how it turns out. :slight_smile:

1 Like

Hi @jacob.small , I tried different steps but still no way :neutral_face:

can you guide me what should I do?

Hi @jacob.small, this is latest code I have tried. For this I’m getting the error unable to close all open transactions phases. Also I have attached two files for your reference.

image

import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

clr.AddReference('RevitAPI')
clr.AddReference('RevitAPIUI')
import Autodesk
from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import *

clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

import os

uiapp = Autodesk.Revit.UI.ExternalCommandData.Application
doc = DocumentManager.Instance.CurrentDBDocument

def sat_to_rfa(sat_file_path, rfa_file_path, rfa_file_name):
    """Convert a SAT file to a RFA file"""
    # Create the document and the application
    app = Autodesk.Revit.ApplicationServices.Application.Create
    familyDoc = doc.Application.NewFamilyDocument(r"C:/ProgramData/Autodesk/RVT 2021/Family Templates/English-Imperial/Specialty Equipment.rft")
    TransactionManager.Instance.EnsureInTransaction(familyDoc)

    shapeImporter = ShapeImporter()
    shapeImporter.InputFormat = ShapeImporterSourceFormat.SAT
    shapes = shapeImporter.Convert(familyDoc, sat_file_path)

    # Save the family document
    Option = SaveAsOptions()
    Option.OverwriteExistingFile = True
    Option.Compact = True
    Option.MaximumBackups = 3
    familyDoc.SaveAs(rfa_file_name, Option)
    TransactionManager.Instance.ForceCloseTransaction()
    familyDoc.Close(False)
  

# Set the paths for the input SAT files and output RFA files
sat_folder_path = r'C:\Users\vramyaa\Revit\input'
rfa_folder_path = r'C:\Users\vramyaa\Revit\output'
sat_files = os.listdir(sat_folder_path)
rfa_files = os.listdir(rfa_folder_path)
# Loop through all the SAT files in the folder
for sat_file_name in sat_files:
    if sat_file_name.endswith('.sat'):
        sat_file_path = os.path.join(sat_folder_path, sat_file_name)
        
        # Generate the corresponding RFA file path
        rfa_file_name = sat_file_name[:-4] + '.rfa'
        rfa_file_path = os.path.join(rfa_folder_path, rfa_file_name)
        
        # Convert the SAT file to the RFA file
        sat_to_rfa(sat_file_path, rfa_file_path, rfa_file_name)
        TransactionManager.Instance.TransactionTaskDone()
        
OUT = sat_files,rfa_files,sat_folder_path, rfa_folder_path, rfa_file_name, rfa_file_path

Part_1.sat (69.3 KB)
Part_2.sat (82.2 KB)

Bit confused here… are you intending to make a new family for each .sat, or add the SAT geometry to existing families?

@jacob.small, my requirement is to create new family for each .sat file

Ok, using the generic model template?

This should accomplish what you’re after. I recommend annotating your code similar to how I have done here next time out so that you are forced to think through what each line is doing.

############################################################
########## configure the Dynam python environment ##########
############################################################
import clr, os # import the CLR (Common language Runtime) and OS modules
clr.AddReference('RevitAPI') #add the Revit API library to the CLR 
from Autodesk.Revit.DB import FilteredElementCollector, ImportPlacement, ImportUnit, SATImportOptions, SaveAsOptions, Transaction, View #import the reelevant sectiosn of the Revit API
clr.AddReference('RevitServices') #add the Revit services library to the CLR 
from RevitServices.Persistence import DocumentManager #import the document manager 
from RevitServices.Transactions import TransactionManager #import the transaciton manager

############################################################
################# get the active applicton #################
############################################################
app = DocumentManager.Instance.CurrentUIApplication.Application #gets the application 

############################################################
############ inputs from the Dynamo environment ############
############################################################
satDir = IN[0] #the directory with the SAT files
rfaDir = IN[1] #the directory for the RFA files
famTemplatePath = IN[2] #the template path to use

############################################################
################## get the sat file paths ##################
############################################################
satPaths = [ "{0}\{1}".format(satDir,f) for f in os.listdir(satDir) if ".sat" in f ]

############################################################
########## create and configure the SaveAsOptions ##########
############################################################
saveOptions = SaveAsOptions() #start a new save as options object
saveOptions.OverwriteExistingFile = True #overwrite any existing file
saveOptions.Compact = True #compact the file
saveOptions.MaximumBackups = 1 #set the number of backups to 1 - you're making a new file so really this could be zero, but that's not an allowed value

############################################################
######## create and configure  the SATImportOptions ########
############################################################
satOpt = SATImportOptions() #start a new sat import options object
satOpt.Placement = ImportPlacement.Origin #set the import placement to the origin
satOpt.Unit = ImportUnit.Foot #set the import unit to feet 
############################################################
	# NOTE: you might want to not always use feet, but 
	# instead pull the units from the SAT itself and find 
	# the assocaited value for the import unit. Look into 
	# the SAT file format  and ImportUnit class of the 
	# Revit API for information on how to do so.
############################################################

############################################################
############# setup the OUT variable as a list #############
############################################################
results = [] #an empty list for now 

############################################################
###### iterate the sat files to generate new families ######
############################################################
for satPath in satPaths: #for every SAT path in the list of sat paths
	# Get the rfa path, and start a new family document 
	rfaPath = satPath[:-4].replace(satDir,rfaDir)+".rfa" #take the SAT path and replace the directory with the RFA directory, and the .sat extention with the .rfa extension
	doc = app.NewFamilyDocument(famTemplatePath) #start a new document from the family template path
	#start a new transaction in the family document
	transaction = Transaction(doc,"import sat") #build a new transaction
	transaction.Start() #start the transaction 
	#get the first 3d view we can find as we'll need a view to run the import in
	view_fec = FilteredElementCollector(doc).OfClass(View) #get all views in the file
	view1 = [v for v in view_fec if str(v.ViewType) == 'ThreeD' and not v.IsTemplate] #for each view in the collector, filter out any which are not a ThreeD type or are a template
	#if a 3d view was found perform the following
	if len(view1) >0:
		view1 = view1[0] #set view1 to the first view found
		#import the SAT and commit the change
		satImport = doc.Import(satPath,satOpt, view1) # import the SAT geometry
		transaction.Commit() #commit the transaction
		#save the family and close it 
		doc.SaveAs(rfaPath, saveOptions) #save the familyw tih the previously defined options 
		doc.Close(False) #close the family document
		results.append(rfaPath) #append the rfa extension to the results list
	#otherwise
	else:
		results.append("No 3d view was found. Please pick a new family template.") #append a message to the results list

############################################################
####### return the results to the Dynamo environment #######
############################################################
OUT = results #set the OUT varaible to the results list 

Hi @jacob.small, Thanks as lot it works as expected.

But during file creation I’m getting this warning, do you have any idea why it is popping up?

I tried the steps manually but I’m not getting any warning. I used Part_1.sat file.

Is it possible to suppress the warning so that the file conversions proceed without any stop?

image