//
// (C) Copyright 2003-2020 by Autodesk, Inc. All rights reserved.
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM 'AS IS' AND WITH ALL ITS FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
//
// Use, duplication, or disclosure by the U.S. Government is subject to
// restrictions set forth in FAR 52.227-19 (Commercial Computer
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Web.Script.Serialization;
using System.Windows;
using Autodesk.Revit.DB;
using Revit.SDK.Samples.CloudAPISample.CS.View;
namespace Revit.SDK.Samples.CloudAPISample.CS.Migration
{
///
/// This is a sample shows how to migrate A360 Teams project to BIM360 Docs
/// with Revit Cloud API
///
public class MigrationToBim360 : SampleContext
{
///
/// File name to store models' guid info.
///
public const string FModelsGuid = "modelsguid.json";
///
/// File name to store linked models' info
///
public const string FLinksInfo = "linkinfo.json";
///
/// Default constructor
///
public MigrationToBim360()
{
Model = new UIMigrationViewModel();
View = new ViewMigrationToBim360(this);
}
///
/// Model for migration case
///
public IMigrationModel Model { get; }
///
public override void Terminate()
{
View = null;
}
private IEnumerable GetLinkInstances(Document document)
{
var links = new FilteredElementCollector(document);
var classFilter = new ElementClassFilter(typeof(RevitLinkType));
links.WherePasses(classFilter);
foreach (var link in links)
if (link is RevitLinkType revitLinkType)
yield return revitLinkType;
}
private FolderLocation GetTargetFolderUrn(IList rules, string directory, string model)
{
foreach (var rule in rules)
{
var models = Directory.GetFiles(directory, rule.Pattern, SearchOption.TopDirectoryOnly);
if (models.Contains(model))
return rule.Target;
}
// If the given model can not match any pattern in rules, use root folder by default.
return Model.AvailableFolders.Last();
}
///
/// User coroutine to update UI during processing.
///
public IEnumerator Upload(string directory, Guid accountId, Guid projectId,
ObservableCollection modelRules)
{
var view = (ViewMigrationToBim360) View;
if (!Directory.Exists(directory))
{
view.UpdateUploadingProgress("Directory doesn't exist.", 0);
yield break;
}
view.UpdateUploadingProgress("Ready to start.", 2);
var models = Directory.GetFiles(directory, "*.rvt", SearchOption.AllDirectories);
var count = 0;
var ops = new OpenOptions
{
OpenForeignOption = OpenForeignOption.DoNotOpen,
DetachFromCentralOption = DetachFromCentralOption.DetachAndPreserveWorksets
};
// Open all documents and analyze if document has links.
// If so, save the relationship info into "mapLocalModelPathToLinksName"
var mapLocalModelPathToLinksName = new Dictionary>();
foreach (var model in models)
{
var name = Path.GetFileName(model);
var progress = count++ * 100 / models.Length;
var modelpath = ModelPathUtils.ConvertUserVisiblePathToModelPath(model);
view.UpdateUploadingProgress($"Analyzing {name}", progress);
yield return null;
try
{
var doc = Application.Application.OpenDocumentFile(modelpath, ops);
var links = new List();
foreach (var linkInstance in GetLinkInstances(doc)) links.Add(linkInstance.Name);
if (links.Count > 0) mapLocalModelPathToLinksName[model] = links;
doc.Close(false);
}
catch (Exception e)
{
var msg = $"Failed to analyze {name} - {e.Message}";
view.UpdateUploadingProgress(msg, progress);
MessageBox.Show(msg);
yield break;
}
yield return null;
}
// All link info should be dump to local file in case something wrong happens during uploading process
JavaScriptSerializer serializer = new JavaScriptSerializer();
var jsonString = serializer.Serialize(mapLocalModelPathToLinksName);
File.WriteAllText(FLinksInfo, jsonString);
view.UpdateUploadingProgress("Analyzing finished", 100);
// Begin Uploading
count = 0;
var mapModelsNameToGuid = new Dictionary();
foreach (var model in models)
{
var name = Path.GetFileName(model);
var progress = count++ * 100 / models.Length;
var modelpath = ModelPathUtils.ConvertUserVisiblePathToModelPath(model);
view.UpdateUploadingProgress($"Uploading {name}", progress);
yield return null;
try
{
var doc = Application.Application.OpenDocumentFile(modelpath, ops);
var folderUrn = GetTargetFolderUrn(modelRules, directory, model)?.Urn;
doc.SaveAsCloudModel(accountId, projectId, folderUrn, $"Migrated_{Path.GetFileName(model)}");
var modelPath = doc.GetCloudModelPath();
mapModelsNameToGuid.Add(name, $"{modelPath.GetProjectGUID()},{modelPath.GetModelGUID()}");
doc.Close(false);
}
catch (Exception e)
{
var msg = $"Failed to upload {name} - {e.Message}";
view.UpdateUploadingProgress(msg, progress);
MessageBox.Show(msg);
yield break;
}
yield return null;
}
jsonString = serializer.Serialize(mapModelsNameToGuid);
File.WriteAllText(FModelsGuid, jsonString);
view.UpdateUploadingProgress("Uploading finished", 100);
}
///
/// Try to open each cloud model and reload if they have links,
/// making that point to cloud path.
///
/// Transit directory where cloud models exist
/// not used
///
public IEnumerator ReloadLinks(string directory, Guid projectId)
{
var view = (ViewMigrationToBim360) View;
if (!Directory.Exists(directory))
{
view.UpdateReloadingProgress("Directory doesn't exist.", 0);
yield break;
}
view.UpdateReloadingProgress("Ready to start.", 2);
var ops = new OpenOptions();
var transOp = new TransactWithCentralOptions();
var swcOptions = new SynchronizeWithCentralOptions();
var count = 0;
// Read mapping info
var jsonString = File.ReadAllText(FLinksInfo);
JavaScriptSerializer serializer = new JavaScriptSerializer();
var mapLocalModelPathToLinksName = serializer.Deserialize>>(jsonString);
jsonString = File.ReadAllText(FModelsGuid);
var mapModelsNameToGuid = serializer.Deserialize>(jsonString);
// Try to open each cloud model and reload if they have links, making that point to cloud path.
foreach (var kvp in mapLocalModelPathToLinksName)
{
var localPath = kvp.Key;
var name = Path.GetFileName(localPath);
var guids = mapModelsNameToGuid[name].Split(',');
var hostModelPath =
ModelPathUtils.ConvertCloudGUIDsToCloudPath("US", Guid.Parse(guids[0]), Guid.Parse(guids[1]));
var progress = count++ * 100 / mapLocalModelPathToLinksName.Count();
view.UpdateReloadingProgress($"Reloading {name}", progress);
yield return null;
try
{
var doc = Application.Application.OpenDocumentFile(hostModelPath, ops,
new DefaultOpenFromCloudCallback());
foreach (var linkInstance in GetLinkInstances(doc))
if (mapModelsNameToGuid.TryGetValue(linkInstance.Name, out var sLinkedGuids))
{
guids = mapModelsNameToGuid[linkInstance.Name].Split(',');
var linkModelPath =
ModelPathUtils.ConvertCloudGUIDsToCloudPath("US", Guid.Parse(guids[0]),
Guid.Parse(guids[1]));
linkInstance.LoadFrom(linkModelPath, new WorksetConfiguration());
}
doc.SynchronizeWithCentral(transOp, swcOptions);
doc.Close(false);
}
catch (Exception e)
{
var msg = $"Failed to reload {name} - {e.Message}";
view.UpdateUploadingProgress(msg, progress);
MessageBox.Show(msg);
yield break;
}
}
view.UpdateReloadingProgress("Reloading finished", 100);
}
}
}