#RTCEUR Wish 5 – Who Created Those Warnings?

To help BIM Managers train or punish their Revit users, I was asked at RTC in Delft if we can use the API to figure out which users are responsible for each warning in your Revit file.

Here’s how that can be done by writing all warnings to a log file and then comparing warnings in the current file with those in the log.

public void RegisterFailureReporter()
{
    Application app = this.Application;
    Document doc = this.ActiveUIDocument.Document;
    if (doc.PathName == "")
    {
        TaskDialog.Show("Error","Please save the file and then repeat this command.");
        return;
    }
    app.FailuresProcessing += FailureReporter;
}

private void FailureReporter(object sender, Autodesk.Revit.DB.Events.FailuresProcessingEventArgs args)
{
    FailuresAccessor fa = args.GetFailuresAccessor();
    Document doc = fa.GetDocument();

    using (StreamWriter sw = new StreamWriter(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), Path.GetFileName(doc.PathName) + " Warning Creation Log.csv"), true))
    {
        foreach (FailureMessageAccessor fma in fa.GetFailureMessages(FailureSeverity.Warning))
        {
            sw.Write(DateTime.Now + "," + fa.GetDocument().Application.Username + ",");
            sw.Write(fma.GetDescriptionText().Replace(Environment.NewLine,"") + "," );
            foreach (ElementId id in fma.GetFailingElementIds())
            {
                // use UniqueId instead of ElementId because the UniqueId is stable across Save To Central while the ElementId property may change.  
                sw.Write(doc.GetElement(id).UniqueId + ",");
            }
            sw.Write(Environment.NewLine);
        }
    }
}

public void CheckCurrentWarningsInLog()
{
    Document doc = this.ActiveUIDocument.Document;
    if (doc.PathName == "")
    {
        TaskDialog.Show("Error","Please save the file and then repeat this command.");
        return;
    }    
    using (StreamReader warningsHTMLReader = new StreamReader(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), Path.GetFileNameWithoutExtension(doc.PathName) + "_Error Report.html")))
    {
        string line = "";
        while ((line = warningsHTMLReader.ReadLine()) != null)
        {
            if (!line.Contains(": id "))
                continue;

            string[] row = System.Text.RegularExpressions.Regex.Split(line, "id ");

            string id1 = row[1].Split(' ')[0];
            string uniqueId1 = doc.GetElement(new ElementId(int.Parse(id1))).UniqueId;
            string uniqueId2 = null;
            if (row.Count() == 3) // there are two element ids in this row
            {
                string id2 = row[2].Split(' ')[0];
                uniqueId2 = doc.GetElement(new ElementId(int.Parse(id2))).UniqueId;
            }

            using (StreamReader warningLogReader = new StreamReader(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), Path.GetFileName(doc.PathName) + " Warning Creation Log.csv")))
            {
                string lineLog = "";
                while ((lineLog = warningLogReader.ReadLine()) != null)
                {
                    if (lineLog.Contains(uniqueId1) || (uniqueId2 != null && lineLog.Contains(uniqueId1) && lineLog.Contains(uniqueId2)))
                    {
                        using (StreamWriter sw = new StreamWriter(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), Path.GetFileName(doc.PathName) + " Current Warnings With User Info.csv"), true))
                        {
                                   sw.WriteLine(lineLog);
                        }
                        break;
                    }
                }
            }
        }
    }
}
Advertisement

#RTCEUR Wish 2 – Highlight and Isolate Warning Elements

@marcellosgamb suggested an API that runs thru every error in a model turns element w/error red then as u fix each error turn normal

There is no API access to the Revit “Review Warnings” data, so this implementation requires the user to export the warning data. The program then imports this HTML, parses it to find the element IDs, and then isolates and colors the elements in two 3D views.

public void ShowWarningElements()
{
    Document doc = this.ActiveUIDocument.Document;

    string filename = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "warnings.html");

    IList<ElementId> ids = new List<ElementId>();

    using (StreamReader sr = new StreamReader(filename))
    {
        string line = "";
        while ((line = sr.ReadLine()) != null)
        {
            string[] row = System.Text.RegularExpressions.Regex.Split(line,"id ");
            if (row.Length != 3)
            {
                continue;
            }
            string id1 = row[1].Split(' ')[0];
            string id2 = row[2].Split(' ')[0];

            ids.Add(new ElementId(int.Parse(id1)));
            ids.Add(new ElementId(int.Parse(id2)));
        }
    }    

    View isolateView = new FilteredElementCollector(doc).OfClass(typeof(View)).Cast<View>().Where(q => q.Name == "IsolateWarningElements").FirstOrDefault();
    View overrideView = new FilteredElementCollector(doc).OfClass(typeof(View)).Cast<View>().Where(q => q.Name == "OverrideWarningElements").FirstOrDefault();

    OverrideGraphicSettings ogs = new OverrideGraphicSettings();
    Color red = new Color(255, 0, 0);
    ogs.SetProjectionLineColor(red);
    ogs.SetProjectionLineWeight(8);

    Element solidFill = new FilteredElementCollector(doc).OfClass(typeof(FillPatternElement)).Where(q => q.Name.Contains("Solid")).First();

    ogs.SetProjectionFillPatternId(solidFill.Id);
    ogs.SetProjectionFillColor(new Color(0, 255,0));

    using (Transaction t = new Transaction(doc, "Isolate/Override Warnings"))
    {
        t.Start();
        isolateView.IsolateElementsTemporary(ids);

        foreach (ElementId id in ids)
        {
            overrideView.SetElementOverrides(id, ogs);
        }
        t.Commit();
    }
}

Using the API to resolve “identical instances in same place” warnings

The API can be used to parse a warnings HTML file exported from Revit and then delete the “less desirable” instance in each pair of instances in the same place.

The instance that gets deleted is the one

  • without a tag (if one instance has a tag)
  • with less instance parameter data

Running rules with the Performance Adviser

With the Performance Adviser introduced in the last post, you can run one or more rules and learn which, if any, are being violated.

Here is a macro that runs the 3 default rules whose names include “View”. In my test model it results in 3 warnings:

ViewWarnings

public void PerfAdviserRunViewRules()
{
    Document doc = this.ActiveUIDocument.Document;
    PerformanceAdviser pa = PerformanceAdviser.GetPerformanceAdviser();

    // this list will contain the rules we want to run
    IList<PerformanceAdviserRuleId> ruleList = new List<PerformanceAdviserRuleId>();
    string s = "";

    // Loop through all rules
    foreach (PerformanceAdviserRuleId ruleId in pa.GetAllRuleIds())
    {
        // If the rule name contains "View" add it to the list
        if (pa.GetRuleName(ruleId).Contains("View"))
            ruleList.Add(ruleId);
    }

    TaskDialog.Show("d",ruleList.Count.ToString());

    // Run all rules in the list and loop through all resutling failure messages
    foreach (FailureMessage message in pa.ExecuteRules(doc,ruleList))
    {
        // for each failure get its description and the names & ids of the associated elements
        s += message.GetDescriptionText() + " " + getElementsFromList(doc, message.GetFailingElements()) + "\n\n";
    }
    TaskDialog.Show("Warnings",s);
}

private string getElementsFromList(Document doc, ICollection<ElementId> ids)
{
    string s = "";
    foreach (ElementId id in ids)
    {
        Element e = doc.GetElement(id);
        s += e.Name + " (" + id + "),";
    }
    // A comma is being added after each element id, but we don't want that comma after the last id
    return s.TrimEnd(',');
}

The default rules of the Performance Adviser

Over at the Revit Forum there is talk about Revit Warnings, which is a good excuse to write about PerformanceAdviser. This class lets you analyze the model and warn the user about issues that might harm performance. You can also create your own warnings based on any condition that you can detect with the API.

There are 17 rules hard-coded into Revit.

Get the rules with this code:

public void PerfAdviserGetAllRules()
{
    PerformanceAdviser pa = PerformanceAdviser.GetPerformanceAdviser();
    string s = "";
    foreach (PerformanceAdviserRuleId ruleId in pa.GetAllRuleIds())
    {
        s += pa.GetRuleName(ruleId) + ": " + pa.GetRuleDescription(ruleId) + "\n\n";
    }
    TaskDialog.Show(pa.GetNumberOfRules() + " Rules", s);
}

The rules are:

rules