I have the method here http://teocomi.com/export-revit-warnings-list-from-api/ and am calling it from an application macro method to export warnings for a folder of rvt files:
public async void ExportWarningHTML()
{
Autodesk.Revit.UI.UIApplication uiapp = this;
Document doc = uiapp.ActiveUIDocument.Document;
// Input Directory
string inputDir = #"C:\input";
// Output Directory
string outputDir = #"C:\output";
//Get files from inputDir
string[] files = Directory.GetFiles(inputDir, "*.rvt");
// Set open options to detach from central and preserve ws
OpenOptions openOptions = new OpenOptions();
openOptions.DetachFromCentralOption = DetachFromCentralOption.DetachAndPreserveWorksets;
// Process each *.rvt file in folder
// Naive approach. DOES NOT WORK.
foreach(string file in files)
{
// Get current doc
var docLast = uiapp.ActiveUIDocument.Document;
// Open new document
var docNext = ActiveUIDocument.Application.OpenAndActivateDocument(file);
// Close last document
docLast.Close(false);
// Export Warnings
var html = await Win32Api.ExportWarinings(uiapp, outputDir);
}
}
}
However this only works for the first file then crashes. How can I modify this code or the linked "ExportWarnings" code I linked to to have this process a folder of .rvt files.
Congratulations on your very nice solution to Export Revit Warnings List From Api!
As you know, the Revit API can only be used within a valid Revit API context. Such a context is provided only within callback functions provided by the Revit API, such as external command Execute. Furthermore, the Revit API is not multi-threading. Making calls to the API outside such a context can lead to a crash. That may well be exactly what you are experiencing.
Therefore, I wonder whether async can be used at all in this context. One possibility to handle these restrictions is by making use of external events:
http://thebuildingcoder.typepad.com/blog/about-the-author.html#5.28
Is this code running in an external command Execute method? If so, how about just removing the async stuff, and simply calling Sleep repeatedly until Revit has finished processing the first file?
No, that will probably not work, and is probably not right at all.
Next suggestion: remove async; make the call to process the next file; when it is done, raise an external event; within the external event, repeat the algorithm to process the next file; etc.
I am very much looking forward to hearing how you resolve this!
Related
I am working on a project where I am required to create multiple revit files, zip them all together and download the zip file as the output in design automation API for Revit.
My project is completely working as expected on my local machine, but when I upload my appbundle and execute my workitem, I get a "failedInstructions" status.
I am not aware whether it is possible to create multiple revit files in design automation API
Following is what I have tried:
This is my workitem json, you see I am passing a json array with sample data for the wall.
"arguments":{
"ElementParams":{
"url":"{'elementdata':[[{'Keys':'Sytème','Value':'Wall_1'},{'Keys':'Thickness','Value':'120.00'},{'Keys':'Length','Value':'2500.00'},{'Keys':'Height','Value':'1200.00'}],[{'Keys':'Sytème','Value':'Wall_2'},{'Keys':'Thickness','Value':'120.00'},{'Keys':'Length','Value':'2500.00'},{'Keys':'Height','Value':'1200.00'}],[{'Keys':'Sytème','Value':'Wall_3'},{'Keys':'Thickness','Value':'120.00'},{'Keys':'Length','Value':'2500.00'},{'Keys':'Height','Value':'1200.00'}],[{'Keys':'Sytème','Value':'Wall_4'},{'Keys':'Thickness','Value':'120.00'},{'Keys':'Length','Value':'2500.00'},{'Keys':'Height','Value':'1200.00'}]]}"
},
"resultFamily":{
"verb":"put",
"url":"https://storage.googleapis.com/bucketname/RevitObjects.zip?XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"headers": {
"Authorization": "",
"Content-Type": "application/octet-stream"
}
}
}
In my code, I create a new document in a loop.
Document doc = app.NewProjectDocument(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\" + "DefaultTemplates.rte");
and save and close the current document
SaveAsOptions options = new SaveAsOptions();
options.OverwriteExistingFile = true;
doc.SaveAs(Assembly.GetExecutingAssembly().Location) + "\\" + fileName, options);
doc.Close(); //Closing the document
List<string> files = Directory.GetFiles(Assembly.GetExecutingAssembly().Location) + "\\").Where(a => a.Contains(Path.GetFileNameWithoutExtension(fileName)) && a.Split('.').Count() > 2).ToList();
foreach (var fl in files)
{
if (File.Exists(fl))
File.Delete(fl);
}
byte[] filebytes = File.ReadAllBytes(GlobalData.TemplatePath + "\\" + GlobalData.DestinationFile);
GlobalData.FileList.Add(GlobalData.DestinationFile, filebytes);
Here fileName is Wall_1, Wall_2, Wall_3 and Wall_4 which will appear in a loop
Here what I am doing is saving the created revit file example Wall_1
After closing the document, there is a copy of the file created example Wall_1.0001.rvt. I delete all the additional files created and keep one final version and add it to file bytes.
The data added in byte[] filebytes is then used to create a zip file of name "RevitObjects.zip"
After which I delete all the files.
This process works perfect on my local machine, but when I execute the workitem the log created states the following:
[07/03/2019 13:47:38] Error: An unexpected error happened during phase CoreEngineExecution of job.
[07/03/2019 13:47:38] Job finished with result FailedExecution
[07/03/2019 13:47:38] Job Status:
{
"status": "failedInstructions",
No other error message is stated in the log.
Let me know if multiple revit document creation is possible in design automation api for revit
Are we not allowed to perform delete operation in the working directory.
I tried by creating a folder and performing the same operation mentioned above, but I got an access denied message.
Let me know where I have gone wrong. Also any guidance to achieve the task will be appreciated
Thank you
First of all, Yes, multiple revit file output is supported by Design Automation system, it's very easy that you just need to set the parameter zip to "true" of your output, and within your plugin, save all your output file to a folder as you named in your output parameter of "localName", please check my blog post at https://forge.autodesk.com/blog/how-generate-dynamic-number-output-design-automation-revit-v3 for all the details.
You are not allowed to access any other folders except the current working folder, you can create subfolder under current working folder and put all your files there.
Hope it helps.
If the json argument is very large, you may provide a "inputParameters.json" as an input file, instead of passing everything in the WorkItem payload
I have used PCL storage package to create a folder for my application. I referred to this. Here is my code sample:
public ListPage()
{
testFile();
Content = new StackLayout
{
Children = {
new Label { Text = "Hello ContentPage" }
}
};
}
async public void testFile()
{
// get hold of the file system
IFolder rootFolder = FileSystem.Current.LocalStorage;
// create a folder, if one does not exist already
IFolder folder = await rootFolder.CreateFolderAsync("MySubFolder", CreationCollisionOption.OpenIfExists);
// create a file, overwriting any existing file
IFile file = await folder.CreateFileAsync("MyFile.txt", CreationCollisionOption.ReplaceExisting);
// populate the file with some text
await file.WriteAllTextAsync("Sample Text...");
}
The folder for files is getting created under sdcard/android/data/ directory but it does not create "MySubFolder" folder under files.
I have set WRITE_EXTERNAL_STORAGE and READ_EXTERNAL_STORAGE for my android project. Am I missing any other configurations?
Having run into similar issues (though on iOS), I now have this working, maybe it helps you. The issues are properly dealing with the async calls and other threading fun.
First, my use case is that I bundle a number of file resources with the app, provided for the user at first run, but from then on updated online. Therefore, I take the bundles resources and copy them into the filesystem proper:
var root = FileSystem.Current.LocalStorage;
// already run at least once, don't overwrite what's there
if (root.CheckExistsAsync(TestFolder).Result == ExistenceCheckResult.FolderExists)
{
_testFolderPath = root.GetFolderAsync(TestFolder).Result;
return;
}
_testFolderPath = await root.CreateFolderAsync(TestFolder, CreationCollisionOption.FailIfExists).ConfigureAwait(false);
foreach (var resource in ResourceList)
{
var resourceContent = ResourceLoader.GetEmbeddedResourceString(_assembly, resource);
var outfile = await _testFolderPath.CreateFileAsync(ResourceToFile(resource), CreationCollisionOption.OpenIfExists);
await outfile.WriteAllTextAsync(resourceContent);
}
Notice the .ConfigureAwait(false). I learned this from the excellent
MSDN Best Practises article on async/await.
Before, I was going back and forth between the method NOT creating directories or files - as in your question - or the thread hanging. The article talks about the latter in detail.
The ResourceLoader class is from here:
Embedded Resource
The ResourceToFile() method is just a helper that turns the long resource names in iOS to shorted file names, as I prefer those. It's not germaine here (IOW: it's a kludge I'm ashamed to show ;)
I think I understand threading better day by day, and if I understand correctly, the art here is to ensure you wait for the async method that load and write files to finish, but make sure you do that on a thread pool that will not deadlock with the main UI thread.
I have a C# single thread application that creates a file. Uses that file and then deletes it. Some times the app has trouble deleting that file. The error I get is:
"The process cannot access the file --file path and file name-- because it is being used by another process."
How can I find out what process has a hold on this file and how can I make that process to let go so that the file can be deleted.
This thing rocks for that very "gotcha".
http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx
Process Monitor v3.05
It has a "Filter" submenu so you can fine tune it to the file that is locked.
You need to post the relevant code so we can see.
It is however always important to make sure that your app close the file that it has opened.
usually something like this will ensure that:
using(var f = File.OpenRead("myfile")) {
...
}
or the equivalent:
try {
var f = File.OpenRead("myfile");
} finally {
f.close()
}
Make sure that you are closing file before delete.
if you are using StreamWriter class make sure that you are closing with its variable
Ex. StreamWriter sw = new StreamWriter();
// some writing operation
sw.Close();
I have created an app that initially creates a database and saves some data in it.
Now I want to delete this database and its files when the user clicks on the reset button but I am getting an error – 'this is use in another process'. I want it to delete and recreate the database when click on the reset button. Any ideas?
The most frequent cause of this is ude to the thread unsafe nature of interacting with isolated storage on Windows Phone. Regardless of how you're implementing the database (be it in a file, or series of files), you're interacting with the isolated storage on some level.
I highly encourage you to read, and make sure you understand this overview of isolated storage before going too far.
You're remark:
This is in use in another process
makes me think you're using a third party library to do your database stuff. This exception/error is being thrown when the library itsself is unable to access isolated storage. Without knowing exactly how you're implementing the database, it's hard to be exactly speak to your situation.
You never "recreate IsolatedStorage", Isolated Storage is a term used to define the collection of disk space your application has access to. Much like a folder, this disk space has a root, and contains only files that you create.
In order to avoid thread exceptions when accessing Isolated Storage, make sure you use the using keyword in C# like so:
namespace IsolatedStorageExample
{
public class ISOAccess
{
// This example method will read a file inside your Isolated Storage.
public static String ReadFile(string filename)
{
string fileContents = "";
// Ideally, you should enclose this entire next section in a try/catch block since
// if there is anything wrong with below, it will crash your app.
//
// This line returns the "handle" to your Isolated Storage. The phone considers the
// entire isolated storage folder as a single "file", which is why it can be a
// little bit of a confusing name.
using(IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForAppliaction())
{
// If the file does not exist, return an empty string
if(file.Exists(filename))
{
// Obtain a stream to the file
using(IsolatedStorageFileStream stream = File.OpenFile(filename, FileMode.Open)
{
// Open a stream reader to actually read the file.
using(StreamReader reader = new StreamReader(stream))
{
fileContents = reader.ReadToEnd();
}
}
}
}
return fileContents;
}
}
}
That should help with your problem of thread safety. To be more specifically helpful toward what you want to do, take a look at the following methods (you can add this to the above class):
// BE VERY CAREFUL, running this method will delete *all* the files in isolated storage... ALL OF THEM
public static void ClearAllIsolatedStorage()
{
// get the handle to isolated storage
using(IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
{
// Get a list of all the folders in the root directory
Queue<String> rootFolders = new Queue<String>(file.GetDirectoryNames());
// For each folder...
while(0 != rootFolders.Count)
{
string folderName = rootFolders.Dequeue();
// First, recursively delete all the files and folders inside the given folder.
// This is required, because you cannot delete a non-empty directory
DeleteFilesInFolderRecursively(file, folderName);
// Now that all of it's contents have been deleted, you can delete the directory
// itsself.
file.DeleteDirectory(rootFolders.Dequeue());
}
// And now we delete all the files in the root directory
Queue<String> rootFiles = new Queue<String>(file.GetFileNames());
while(0 != rootFiles.Count)
file.DeleteFile(rootFiles.Dequeue());
}
}
private static void DeleteFilesInFolderRecursively(IsolatedStorageFile iso, string directory)
{
// get the folders that are inside this folder
Queue<string> enclosedDirectories = new Queue<string>(iso.GetDirectoryNames(directory));
// loop through all the folders inside this folder, and recurse on all of them
while(0 != enclosedDirectories.Count)
{
string nextFolderPath = Path.Combine(directory, enclosedDirectories.Dequeue());
DeleteFilesInFolderRecursively(nextFolderPath);
}
// This string will allow you to see all the files in this folder.
string fileSearch = Path.Combine(directory, "*");
// Getting the files in this folder
Queue<string> filesInDirectory = iso.GetFileNames(fileSearch);
// Finally, deleting all the files in this folder
while(0 != filesInDirectory.Count)
{
iso.DeleteFile(filesInDirectory.Dequeue());
}
}
Another thing I highly recommend is implementing the class that accesses IsolatedStorage using a "Multithreaded Singleton Pattern" as described here.
Hope that's helpful. Code is provided "as-is", I have not compiled it, but the general concepts are all there, so if there's something amiss, read the MSDN docs to see where I goofed. But I assure you, most of this is copied from functional code of mine, so it should work properly with very little fanagaling.
I have the following code where i save the xml file into this particular location shown below
public bool GetList(string keyword1, string streetname, string lat, string lng, string radius)
{
XmlDocument xmlDoc= CreateXML( keyword1,streetname,lat,lng,radius);
xmlDoc.Save(#"C:\Documents and Settings\Vijay.EKO-03\Desktop\blockseek3-9-2010\Block3.xml");
return true;
}
This Block3.xml file gets stored in my application folder, i refer to that particular Block3.xml using this code
function searchLocationsNear()
{
var searchUrl = "Block3.xml";
GDownloadUrl(searchUrl, function(data) {
var xml = GXml.parse(data);
var markers = xml.documentElement.getElementsByTagName('marker');
map.clearOverlays();
I am able to parse that Block3.xml and display results but my problem is during second iteration again when i try to save Block3.xml
xmlDoc.Save(#"C:\Documents and Settings\Desktop\blockseek3-9-2010\Block3.xml");
The previous Block3.xml file gets replaced by new one and gets stored in application folder
when i execute var searchUrl="Block3.xml"; it reads the first Block3.xml not the replaced one ,can any one help to tackle this code .
Is their any syntax to clear the saved xmldoc file in that particular folder .
Most likely, the GXml class retains an open file handle. Check the documentation: if it implements IDisposable, wrap your processing code like this:
using ( var xml = GXml.parse(data) )
{
var markers = xml.documentElement.getElementsByTagName('marker');
map.clearOverlays();
}
This also assumes that the parse() method is the one that actually loads and reads the file. If you don't release the file handle, your process doesn't know that there's another version in the file system. (And other nasty stuff can happen, depending on the exact mode the O/S opened the file in.)
I trust your production code won't contain these hard coded paths...?