This post has been overdue – this is in continuation to my previous post on this topic and also to wrap up my first phase of experiential learning with the Cloud.
In this post, I will try to elaborate on the app that I created to put this learning to use and also picked up a simple use-case where this app might eventually be useful as well.
So at first, the use-case:
Let’s assume - I am an Architect (well atleast I did study Architecture during my undergrad degree) designing a 500 bed hospital project for the last couple of months. We are at the last stages of design and am currently working with a Autodesk Ecotect Analysis to do a detailed study of my design and its environmental analysis. I woke up this morning and realized that I had to split one of the big rooms into small units (design being such an iterative process, this is not a surprise at all) and so while having breakfast, I use Revit on my home PC and make the changes to the model (let's assume have got it off the file sharing server we have in our Design office). I have the GbXMLCloudSync app installed too and this app exports the Revit model to GbXML format while pushing it to the cloud, every time I do some changes in the model and save the changes. After completing the design changes, I drop my son to his preschool, head to the office and grab the latest GbXML file from the Cloud using this app in Revit and resume my work with Autodesk Ecotect Analysis. I did not have to worry about locating and working with the latest and most updated version of the GbXML file of my model (so ensured accessibility and version control) and neither was there any fear of losing the data since it was all safely and securely stored in the Cloud (safe and secure storage).
I hope this use-case sounded real/convincing enough!
Time for a quick video, demonstrating the app:
After discussing over the use-case, lets us look at how we can create the simple app which leverages the Cloud services – to store the file (GbXML) as a Blob.
To trigger the export of the Revit model to the GbXML format everytime the model is saved, I subscribed to the DocumentSaved event in the OnStartUp event of Revit and this is also where the ribbon button for the app gets created, as shown below -
Result IExternalApplication.OnShutdown(
UIControlledApplication application)
{
application.ControlledApplication.DocumentSaved -=
new EventHandler<DocumentSavedEventArgs>(
ControlledApplication_DocumentSaved);
return Result.Succeeded;
}
Result IExternalApplication.OnStartup(
UIControlledApplication application)
{
application.ControlledApplication.DocumentSaved +=
new EventHandler<DocumentSavedEventArgs>(
ControlledApplication_DocumentSaved);
try
{
// create a Ribbon panel which contains three
// stackable buttons and one single push button
string firstPanelName = "BIM Cloud Sync";
RibbonPanel panel = application.CreateRibbonPanel(
firstPanelName);
// set the information about the command we will
// be assigning to the button
PushButtonData pushButtonData = new PushButtonData(
"GbXMLCloudSync",
"GbXML Cloud Sync",
AddInPath,
"GbXMLCloudSync.Command");
//' add a button to the panel
PushButton pushButton = panel.AddItem(pushButtonData)
as PushButton;
//' add an icon
pushButton.LargeImage = LoadPNGImageFromResource(
"GbXMLCloudSync.upgrade_32by32.png");
// add a tooltip
pushButton.ToolTip =
"Set the Options for the BIM model sync feature with" +
" various formats in the Cloud!";
// long description
pushButton.LongDescription =
"Specify whether the app will sync with the BIM in "+
"the Cloud with every save or with every update to " +
"the model. The formats that are available on the " +
"Cloud is GBXML versions of the active model.";
string path;
path = System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.
GetExecutingAssembly().Location);
return Autodesk.Revit.UI.Result.Succeeded;
}
catch (Exception ex)
{
TaskDialog.Show(ex.ToString(), "BIM Cloud Sync Ribbon");
return Autodesk.Revit.UI.Result.Failed;
}
}
/// <summary>
/// Load the bitmap from resource. The resource image
/// 'Build action' must be set to 'Embeded resource'.
/// PNG extension supports transparency.
/// </summary>
/// <param name="imageResourceName">Resource name .PNG</param>
/// <returns>The loaded image</returns>
private static System.Windows.Media.ImageSource
LoadPNGImageFromResource(string imageResourceName)
{
System.Reflection.Assembly dotNetAssembly =
System.Reflection.Assembly.GetExecutingAssembly();
System.IO.Stream iconStream =
dotNetAssembly.GetManifestResourceStream(imageResourceName);
System.Windows.Media.Imaging.PngBitmapDecoder bitmapDecoder =
new System.Windows.Media.Imaging.PngBitmapDecoder(iconStream,
System.Windows.Media.Imaging.BitmapCreateOptions.
PreservePixelFormat, System.Windows.Media.Imaging.
BitmapCacheOption.Default);
System.Windows.Media.ImageSource imageSource =
bitmapDecoder.Frames[0];
return imageSource;
}
In the DocumentSaved event handler, all that needed to be done was to export the Revit model to a GBXML file using the Document.Export() method. For the sake of simplicity, the exported GbXML file is saved to a particular location in the Temp folder. If desired, we can also add a line of code to delete this temporary GbXML file after it has been pushed to the Cloud.
void ControlledApplication_DocumentSaved(object sender,
DocumentSavedEventArgs e)
{
if (withEverySave)
{
Export(e.Document);
}
}
private void Export(Document doc)
{
try
{
Transaction transaction =
new Transaction(doc, "Export_To_GBXML");
transaction.Start();
GBXMLExportOptions options = new GBXMLExportOptions();
bool exported = doc.Export("C:\\Temp\\", "Sample", options);
transaction.Commit();
pushToCloud(doc);
//TaskDialog.Show("Export",
// "This is where are exporting to the Cloud!");
}
catch (System.Exception ex)
{
TaskDialog.Show("Exception", ex.Message);
}
}
And after the export, the file is pushed to the cloud using the Windows Azure Blob Storage Service. You can read up on the basics on Storing any file/content to Azure as a Blob in this guide.
public static string connStr =
"DefaultEndpointsProtocol=https; " +
"AccountName=bimsync; " +
"AccountKey=<accountkey>";
void pushToCloud(Document doc)
{
try
{
var storageAccount = CloudStorageAccount
.Parse(connStr);
// Create the blob client
CloudBlobClient blobClient =
storageAccount.CreateCloudBlobClient();
containerName = doc.Application.Username.ToLower();
blobName = doc.Title.ToLower();
// Retrieve a reference to a container
CloudBlobContainer container =
blobClient.GetContainerReference(containerName);
// Create the container if it doesn't already exist
container.CreateIfNotExist();
// Retrieve reference to a blob named after title of the RVT
CloudBlob blob = container.GetBlobReference(blobName);
// Create or overwrite the local file
using (var fileStream = System.IO.File.OpenRead(
@"C:\Temp\Sample.xml"))
{
blob.UploadFromStream(fileStream);
}
}
catch(System.Exception exm)
{
TaskDialog.Show("PushToCloud Ex", exm.Message);
}
}
Before we proceed with the code, in case you are wondering how to get the connection string used in the code snippet above, you can login to Windows Azure Platform and go to the Hosted Services, Storage Accounts & CDN and on selecting the Storage Account (I am asuming you have already created a Storage account), you can see the Primary Access Key in the Properties panel. A screenshot is shown below. This is the same string that should be entered in the <accountkey> field in the code.
With this, we have looked at the code which exports the Revit model to GbXML format as a temporary file and then pushes it to the Windows Azure Blob Storage Service.
The other part of the app retrieves a selected Blob, lists all the blobs in the container (which is defined by the user name in Revit) and delete a selected blob. The blob name, as you might have noticed already, is based on the Revit file name itself.
The (self-explanatory) code which covers all of the above mentioned functionality is included below:
private static void CloudPlubming()
{
try
{
string connStr = "DefaultEndpointsProtocol=https; " +
" AccountName=bimsync; " +
"AccountKey=<accountKey>";
// Retrieve storage account from connection-string
var storageAccount = CloudStorageAccount
.Parse(connStr);
// Create the blob client
CloudBlobClient blobClient =
storageAccount.CreateCloudBlobClient();
containerName =
_cmdData.Application.Application.Username.ToLower();
// Retrieve reference to a previously created container
container = blobClient.GetContainerReference(containerName);
}
catch (System.Exception ex)
{
MessageBox.Show("CloudPlumbing", ex.Message);
}
}
private void btnDelete_Click(object sender, EventArgs e)
{
if (listProjects.SelectedItems.Count > 0
&& !listProjects.SelectedItems[0].SubItems[0].Text.Equals(
"None"))
{
CloudPlubming();
string[] Split =
listProjects.SelectedItems[0].SubItems[0].Text.Split(
new Char[] { '/' });
// Retrieve reference to a blob named after title of the RVT
CloudBlob blob = container.GetBlobReference(
Convert.ToString(Split[2]));
// Delete the blob
blob.Delete();
ListAllBlobs();
}
else
{
System.Windows.Forms.MessageBox.Show("Invalid selection!");
}
}
private void btnOpen_Click(object sender, EventArgs e)
{
if (listProjects.SelectedItems.Count > 0
&& !listProjects.SelectedItems[0].SubItems[0].Text.Equals(
"None"))
{
CloudPlubming();
string[] Split =
listProjects.SelectedItems[0].SubItems[0].Text.Split(
new Char[] { '/' });
// Retrieve reference to a blob named after title of the RVT
CloudBlob blob = container.GetBlobReference(
Convert.ToString(Split[2]));
// Save blob contents to disk
using (var fileStream = System.IO.File.OpenWrite(
@"C:\Temp\Sample.xml"))
{
blob.DownloadToStream(fileStream);
}
// Open the GBXML file
System.Diagnostics.Process.Start(@"C:\Temp\Sample.xml");
}
else
{
System.Windows.Forms.MessageBox.Show("Invalid selection!");
}
}
private void ListAllBlobs()
{
listProjects.Items.Clear();
CloudPlubming();
if (container.ListBlobs().Count() > 0)
{
// Loop over blobs within the container and output
// the URI to each of them
foreach (var blobItem in container.ListBlobs())
{
listProjects.Items.Add(blobItem.Uri.AbsolutePath);
}
}
else
{
listProjects.Items.Add("None");
}
}
private void btnList_Click(object sender, EventArgs e)
{
ListAllBlobs();
}
The complete VS project with the source code for this app can be downloaded from here.
Hope this simple app proves to be a good starting point in your experimentation with our product APIs and leveraging the generic Cloud services for the various workflows that is now possible – time to stretch our imagination!