Macro Examples
While developing eVolve's Macro Automation feature, we created many sample use cases for testing. We realize many of the examples were industry requests and could be impactful. Below is a list of these for customers to try out and modify for their needs.
Pay special consideration to the GetEvAdded, GetEvMod, and the GetEvAll methods. These allow access to the elements that meet the rule criteria without the need to implement the Selection.GetElementIds(). Samples are shown with GetElementIds being used only to allow their functionality without an Automation Rule applied.
These are delivered in as-is condition and are intended to be merely samples. To use, copy any of the functions below into your document macro module and make any needed modificationsl.
Automation Rules.
/*
* Created and Maintained by: James Simpson
* Purpose: Collection of Macros to further drive Revit/eVolve MEP Functionality
* WARNING: Do not attempt to modify unless knowlegable of C# and Revit API
* Disclaimer: Code is delivered "as-is" and is not tested for a production enviroment.
*/
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace eVolve_Examples
{
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
[Autodesk.Revit.DB.Macros.AddInId("244D199E-633B-446A-942E-1F0D96AC04E5")]
public partial class ThisDocument
{
#region Startup Shutdown Methods
private void Module_Startup(object sender, EventArgs e)
{
}
private void Module_Shutdown(object sender, EventArgs e)
{
}
private void InternalStartup()
{
this.Startup += new System.EventHandler(Module_Startup);
this.Shutdown += new System.EventHandler(Module_Shutdown);
}
public void test()
{
var addedIds = GetEvAdded("test");
var modifiedIds = GetEvMod("test");
var all = GetEvAll("test");
TaskDialog.Show("eVolve Macro Automation Sample",
"Element Ids:"
+ "\n All = " + string.Join(", ", all.Select(id => id.IntegerValue.ToString()))
+ "\n Added = " + string.Join(", ", addedIds.Select(id => id.IntegerValue.ToString()))
+ "\n Modified = " + string.Join(", ", modifiedIds.Select(id => id.IntegerValue.ToString())),
TaskDialogCommonButtons.Ok);
}
#endregion Startup Shutdown Methods
/// <summary>
/// Disconnects rod from its structure host and sets the rod length to zero
/// </summary>
public void DetacthRod()
{
var doc = this.Document;
var sels = this.Selection.GetElementIds();
using (var ts = new Transaction(doc, "Detatch Rod"))
{
ts.Start();
foreach (var eid in sels)
{
var e = doc.GetElement(eid);
if (!((FabricationPart)e).IsAHanger())
return;
var rdInfo = ((FabricationPart)e).GetRodInfo();
var sharedp = e.LookupParameter("eM_Detach");
if (sharedp.AsInteger() != 1) continue;
rdInfo.CanRodsBeHosted = false;
rdInfo.SetRodLength(0, 0);
}
ts.Commit();
}
}
/// <summary>
/// Writes correct elevation prefix as well as elevation depending on parts insulatoin properties.
/// </summary>
public void InsulationTags()
{
var doc = this.Document;
var sels = this.Selection.GetElementIds();
using (var ts = new Transaction(doc, "Set Insulation Tag"))
{
ts.Start();
foreach (var eid in sels)
{
var e = doc.GetElement(eid);
var sharedp = e.LookupParameter("eM_Elev");
var elevp = e.get_Parameter(BuiltInParameter.FABRICATION_BOTTOM_OF_PART);
var param = ((FabricationPart)e).InsulationThickness;
if (param > 0)
sharedp.Set("BOI=" + elevp.AsValueString());
else if (e.Category.Name == "MEP Fabrication Pipework")
sharedp.Set("BOP=" + elevp.AsValueString());
else if (e.Category.Name == "MEP Fabrication Ductwork")
sharedp.Set("BOD=" + elevp.AsValueString());
}
ts.Commit();
}
}
/// <summary>
/// Adds 6 inches to the part in the model if FieldCut is checked
/// </summary>
public void AddFieldCut()
{
var doc = this.Document;
var sels = this.Selection.GetElementIds();
using (var ts = new Transaction(doc, "Add Field Cut"))
{
ts.Start();
foreach (var eid in sels)
{
var e = doc.GetElement(eid);
var sharedp = e.LookupParameter("Field Cut");
if (sharedp.AsInteger() != 1)
continue;
var elevp = e.get_Parameter(BuiltInParameter.FABRICATION_PART_LENGTH);
elevp.Set(elevp.AsDouble() + .5);
}
ts.Commit();
}
}
/// <summary>
/// Calculates the angle of a tap from its host. Uses sever utility methods to accomplish this
/// </summary>
public void TapAngle()
{
var doc = this.Document;
var sels = this.Selection.GetElementIds();
using (var ts = new Transaction(doc, "GetTapAngle"))
{
ts.Start();
foreach (var eid in sels)
{
var tap = doc.GetElement(eid);
if (!((FabricationPart)tap).IsATap())
return;
var tapconns = GetConnectors(tap);
var host = GetHost(tap);
if (host == null)
{
return;
}
var hostconns = GetConnectors(host);
var radangle = CalcTapAngle(hostconns, tapconns);
var angle = radangle * (180 / Math.PI);
angle = NormalAng(angle);
var sharedp = tap.LookupParameter("eM_Tap Angle");
sharedp.Set(Math.Round(angle));
}
ts.Commit();
}
}
/// <summary>
/// Gets and writes the room located at the element center to eM_Room
/// </summary>
public void WriteRoom()
{
Document doc = this.Document;
var sels = this.Selection.GetElementIds();
using (var ts = new Transaction(doc, "WriteRoom"))
{
ts.Start();
foreach (var eid in sels)
{
var e = doc.GetElement(eid);
XYZ eloc = (e.get_BoundingBox(doc.ActiveView).Max + e.get_BoundingBox(doc.ActiveView).Min) / 2;
var room = doc.GetRoomAtPoint(eloc);
var sharedp = e.LookupParameter("eM_Room");
sharedp.Set(room.Name);
}
ts.Commit();
}
}
/// <summary>
/// Gets and writes the space located at the element center to eM_Space
/// </summary>
public void WriteSpace()
{
Document doc = this.Document;
var sels = this.Selection.GetElementIds();
using (var ts = new Transaction(doc, "WriteSpace"))
{
ts.Start();
foreach (var eid in sels)
{
var e = doc.GetElement(eid);
XYZ eloc = (e.get_BoundingBox(doc.ActiveView).Max + e.get_BoundingBox(doc.ActiveView).Min) / 2;
var space = doc.GetSpaceAtPoint(eloc);
var sharedp = e.LookupParameter("eM_Space");
sharedp.Set(space.Name);
}
ts.Commit();
}
}
/// <summary>
/// Gets and writes the Scopebox located at the element center to eM_Scope Box
/// </summary>
public void WriteScopeBox()
{
var doc = this.Document;
var sels = this.Selection.GetElementIds();
using (var ts = new Transaction(doc, "WriteScopeBox"))
{
ts.Start();
foreach (var eid in sels)
{
var e = doc.GetElement(eid);
var fcollect = new FilteredElementCollector(doc, doc.ActiveView.Id)
.OfCategory(BuiltInCategory.OST_VolumeOfInterest)
.ToList();
foreach (var elem in fcollect)
{
var sb = elem.get_BoundingBox(doc.ActiveView);
XYZ eloc = ((e.get_BoundingBox(doc.ActiveView).Max) + (e.get_BoundingBox(doc.ActiveView).Min)) / 2;
if (sb.Min.X < eloc.X && sb.Min.Y < eloc.Y && sb.Max.X > eloc.X && sb.Max.Y > eloc.Y)
{
var sharedp = e.LookupParameter("eM_Scope Box");
sharedp.Set(elem.Name);
}
}
}
ts.Commit();
}
}
/// <summary>
/// Determines if hanger needs a CalSil and if so, writes relavant information
/// </summary>
public void WriteCalSil()
{
var doc = this.Document;
var sels = this.Selection.GetElementIds();
using (var ts = new Transaction(doc, "WriteCalSil"))
{
ts.Start();
foreach (var eid in sels)
{
var e = doc.GetElement(eid);
var hostId = ((FabricationPart)e).GetHostedInfo().HostId;
if (hostId.IntegerValue == -1)
return;
var hoste = doc.GetElement(hostId);
var sharedp = e.LookupParameter("eM_CalSil Size");
if (((FabricationPart)hoste).InsulationThickness == 0)
{
sharedp.Set("0");
}
else
{
var ind = ((FabricationPart)hoste).get_Parameter(BuiltInParameter.FABRICATION_PRODUCT_ENTRY);
var outd = ((FabricationPart)hoste).get_Parameter(BuiltInParameter.RBS_PIPE_OUTER_DIAMETER);
sharedp.Set(ind.AsString() + "ID , " + outd.AsValueString() + "OD");
}
}
ts.Commit();
}
}
/// <summary>
/// Ensures rod offset is set to 1" and also runs the eM Bearer Rounding Command.
/// </summary>
public void StandardStrut()
{
var doc = this.Document;
var sels = this.Selection.GetElementIds();
using (var ts = new Transaction(doc, "Standard Strut"))
{
ts.Start();
foreach (var eid in sels)
{
var e = doc.GetElement(eid);
if (!((FabricationPart)e).IsAHanger())
return;
var dims = ((FabricationPart)e).GetDimensions();
NormalizeRodOffset(e, dims);
}
ts.Commit();
}
WidthRoundingeM(sels);
}
/// <summary>
/// Evaluates and sets the rod offset of a hanger to 1"
/// </summary>
/// <param name="e">Element to eval</param>
/// <param name="dims">List of fabrication dims</param>
/// <returns></returns>
public bool NormalizeRodOffset(Element e, IList<FabricationDimensionDefinition> dims)
{
foreach (var rodoffDef in dims.Where(rodoffDef => rodoffDef.Name.Contains("Rod Offset")))
{
var rodoffVal = ((FabricationPart)e).GetDimensionValue(rodoffDef);
if ((rodoffVal * 12) <= 1 / 12)
continue;
((FabricationPart)e).SetDimensionValue(rodoffDef, .0833);
}
return true;
}
/// <summary>
/// Sets rod length of the element to zero
/// </summary>
/// <param name="e">Element to eval</param>
/// <param name="dims">List of fabrication dims</param>
/// <returns></returns>
public bool SetRodZero(Element e, IList<FabricationDimensionDefinition> dims)
{
foreach (var rodlenDef in dims.Where(rodlenDef => rodlenDef.Name.Contains("Length")))
{
var rodlenVal = ((FabricationPart)e).GetDimensionValue(rodlenDef);
if (rodlenVal > 0)
continue;
((FabricationPart)e).SetDimensionValue(rodlenDef, 0);
}
return true;
}
/// <summary>
/// Runs the eM Bear Width Rounding Command
/// </summary>
/// <param name="sel">List of elements to run the command on</param>
private void WidthRoundingeM(ICollection<ElementId> sel)
{
var uiapp = new UIApplication(this.Document.Application);
Selection.SetElementIds(sel);
try
{
var cmdId = RevitCommandId.LookupCommandId(
"CustomCtrl_%CustomCtrl_%CustomCtrl_%eVolve Mechanical%asti-Hangers%" +
"MultiService%eVolveMechanical-Bearer Width Rounding");
uiapp.PostCommand(cmdId);
return;
}
catch
{
return;
}
}
/// <summary>
/// Syncs the host service to the point layer as well as populates the point description via parameter values
/// </summary>
public void SleevePointInfo()
{
var doc = this.Document;
var sels = this.Selection.GetElementIds();
using (var ts = new Transaction(doc, "Sleeve Point Info"))
{
ts.Start();
foreach (var eid in sels)
{
var e = doc.GetElement(eid);
var pntId = ((FamilyInstance)e).GetSubComponentIds().FirstOrDefault(id => doc.GetElement(id).Name.Contains("Point"));
var pnt = doc.GetElement(pntId);
Parameter oHosParameter = e.LookupParameter("eM_Service Name");
Parameter oNestedParameter = pnt.LookupParameter("eM_Service Name");
if (string.IsNullOrEmpty(oNestedParameter.AsString()))
oNestedParameter.Set(oHosParameter.AsString());
Parameter pntdesc = e.LookupParameter("eM_Point Description");
if (string.IsNullOrEmpty(pntdesc.AsString()))
pntdesc.Set(e.LookupParameter("Family").AsValueString() + e.LookupParameter("Size").AsString());
}
ts.Commit();
}
}
/// <summary>
/// Gets and writes the wall information to the hosted element center to eM_Room
/// </summary>
public void WallInfo()
{
var doc = this.Document;
var sels = this.Selection.GetElementIds();
using (var ts = new Transaction(doc, "Device Info"))
{
ts.Start();
foreach (var eid in sels)
{
var e = doc.GetElement(eid);
var wall = ((FamilyInstance)e).Host;
Parameter familyType = wall.get_Parameter(BuiltInParameter.ELEM_FAMILY_AND_TYPE_PARAM);
Parameter wallType = e.LookupParameter("eM_WallType");
wallType.Set(familyType.AsValueString());
Parameter wallThickness = e.LookupParameter("eM_WallThickness");
var wallWidth = (wall as Wall).Width;
wallThickness.Set(wallWidth.ToString());
}
ts.Commit();
}
}
/// <summary>
/// Evaluates elements locations and if they are identical writes Duplicate to eM_Overkill
/// </summary>
public void Overkill()
{
var doc = this.Document;
using (var ts = new Transaction(doc, "Overkill"))
{
ts.Start();
var sels = this.Selection.GetElementIds();
foreach (var eid in sels)
{
var e = doc.GetElement(eid);
var fcollect = new FilteredElementCollector(doc, doc.ActiveView.Id)
.OfCategory((BuiltInCategory)e.Category.Id.IntegerValue)
.Where(fname =>
fname.Name.Equals(e.Name, StringComparison.InvariantCultureIgnoreCase) &&
!fname.UniqueId.Equals(e.UniqueId))
.ToList();
foreach (var elem in fcollect)
{
XYZ eLoc = (e.get_BoundingBox(doc.ActiveView).Max + e.get_BoundingBox(doc.ActiveView).Min) / 2;
var elemLoc = (elem.get_BoundingBox(doc.ActiveView).Max +
elem.get_BoundingBox(doc.ActiveView).Min) / 2;
if (!eLoc.IsAlmostEqualTo(elemLoc))
continue;
var sharedp = elem.LookupParameter("eM_Overkill");
sharedp.Set("Duplicate");
}
}
ts.Commit();
}
}
#region Util Methods
/// <summary>
/// Gets connectors from the passed element
/// </summary>
/// <param name="e">The element to get the connectors of</param>
/// <returns></returns>
private static List<Connector> GetConnectors(Element e)
{
var cs = ((FabricationPart)e).ConnectorManager.Connectors;
return cs.Cast<Connector>().ToList();
}
/// <summary>
/// Gets the host of a given element
/// </summary>
/// <param name="e">The element to get the host from</param>
/// <returns></returns>
private static Element GetHost(Element e)
{
var cs = ((FabricationPart)e).ConnectorManager.Connectors;
foreach (Connector conn in cs)
{
foreach (Connector cRef in conn.AllRefs)
{
var elem = cRef.Owner;
var elemcs = ((FabricationPart)elem).ConnectorManager.Connectors;
if (elemcs.Size > 2)
{
return elem;
}
}
}
return null;
}
/// <summary>
/// Uses the connector directions to determine the angle of a tap from host
/// </summary>
/// <param name="hostends">The connectors of the host</param>
/// <param name="tapcons">The connectors of the tap</param>
/// <returns></returns>
private static double CalcTapAngle(IReadOnlyList<Connector> hostends, IReadOnlyList<Connector> tapcons)
{
var hostDir = hostends[0].CoordinateSystem.BasisZ.Negate(); //Or hostCon2.CoordinateSystem.BasisZ
var hostDownDir = hostends[1].CoordinateSystem.BasisY.Negate();
var hostRightDir = hostends[0].CoordinateSystem.BasisX;
var tapDir = tapcons[1].CoordinateSystem.BasisZ;
var tapRotation = hostRightDir.AngleOnPlaneTo(tapDir, hostDir);
return tapRotation;
}
/// <summary>
/// Normalizes an angle to one in between 0-90
/// </summary>
/// <param name="angle">The angle to normalize</param>
/// <returns></returns>
private static double NormalAng(double angle)
{
if (angle < 90)
return Math.Abs(90 - angle);
if (angle > 90 && angle < 180)
return Math.Abs(180 - angle);
if (angle > 180 && angle < 270)
return Math.Abs(270 - angle);
if (angle > 270 && angle < 360)
return Math.Abs(360 - angle);
return 0;
}
/// <summary>
/// Retrieves added element ids based on criteria from a Macro Automation Rule
/// </summary>
/// <param name="MacroAutomationRuleName"> Name of Macro Automation Rule. Used to find relavant file with affected elements</param>
/// <returns></returns>
private ElementId[] GetEvAdded(string MacroAutomationRuleName)
{
// Extract all the added and modified id's impacted by this rule.
var elementIdFile = System.IO.Path.Combine(System.IO.Path.GetTempPath(), string.Format("eV-{0}.txt", MacroAutomationRuleName));
if (!System.IO.File.Exists(elementIdFile))
{
//null check
return null;
}
// File contains added ids on the first line and modified on the second.
var elementIds = System.IO.File.ReadAllText(elementIdFile).Split(new string[] { Environment.NewLine }, StringSplitOptions.None)
.Select(ids => ids.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries))
.ToArray();
//return only those added
return elementIds[0].Select(id => new ElementId(int.Parse(id))).ToArray();
}
/// <summary>
/// Retrieves modified element ids based on criteria from a Macro Automation Rule
/// </summary>
/// <param name="MacroAutomationRuleName"> Name of Macro Automation Rule. Used to find relavant file with affected elements</param>
/// <returns></returns>
private ElementId[] GetEvMod(string MacroAutomationRuleName)
{
// Extract all the added and modified id's impacted by this rule.
var elementIdFile = System.IO.Path.Combine(System.IO.Path.GetTempPath(), string.Format("eV-{0}.txt", MacroAutomationRuleName));
if (!System.IO.File.Exists(elementIdFile))
{
//null check
return null;
}
// File contains added ids on the first line and modified on the second.
var elementIds = System.IO.File.ReadAllText(elementIdFile).Split(new string[] { Environment.NewLine }, StringSplitOptions.None)
.Select(ids => ids.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries))
.ToArray();
//return only modified
return elementIds[1].Select(id => new ElementId(int.Parse(id))).ToArray();
}
/// <summary>
/// Retrieves all element ids affected based on criteria from a Macro Automation Rule
/// </summary>
/// <param name="MacroAutomationRuleName"> Name of Macro Automation Rule. Used to find relavant file with affected elements</param>
/// <returns></returns>
private ElementId[] GetEvAll(string MacroAutomationRuleName)
{
var modifiedIds = Array.Empty<ElementId>();
var addedIds = Array.Empty<ElementId>();
// Extract all the added and modified id's impacted by this rule.
var elementIdFile = System.IO.Path.Combine(System.IO.Path.GetTempPath(), string.Format("eV-{0}.txt", MacroAutomationRuleName));
if (!System.IO.File.Exists(elementIdFile))
{
//null check
return null;
}
// File contains added ids on the first line and modified on the second.
var elementIds = System.IO.File.ReadAllText(elementIdFile).Split(new string[] { Environment.NewLine }, StringSplitOptions.None)
.Select(ids => ids.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries))
.ToArray();
addedIds = elementIds[0].Select(id => new ElementId(int.Parse(id))).ToArray();
modifiedIds = elementIds[1].Select(id => new ElementId(int.Parse(id))).ToArray();
//return all
return addedIds.Concat(modifiedIds).ToArray();
}
#endregion Util Methods
}
}