Disable error with 'Error' severity

tomerFUNPJ
Enthusiast
Enthusiast

Disable error with 'Error' severity

tomerFUNPJ
Enthusiast
Enthusiast

Hello,

 

I have a specific failure that I want to disable.

I'm able to 'catch' the error (With FailuresProcessing event).

The problem is that I can't find a way to disable / delete the error.

DeleteWarning only works for 'Warning' severity, and my failure is 'Error' severity.

 

Thanks in advance!

0 Likes
Reply
Accepted solutions (1)
2,949 Views
28 Replies
Replies (28)

aignatovich
Advisor
Advisor

Pick one - choose either application level failure processor (as for me - it's a bad choice) or failure processing event.

 

Could you prepare a simple reproducible case: addin + model + what to do to reproduce?

0 Likes

tomerFUNPJ
Enthusiast
Enthusiast

Yep, I'm using failure processing event.

I am doing multiple things but let's focus only on the problem:
The goal is this - When the user tries to delete a part (That was created in the past by my code and has a scheme I made), raise a popup saying he can't do that and block the deletion. I am doing that successfully!
In addition to blocking the deletion, I don't want the error message I attached yesterday to be shown, so there won't be a confusion.

 

To block the deletion I have a class which inherits from IUpdater and uses a FailureDefinition GUID that I registered when Revit loads:

public class SplitElementUpdater : IUpdater
    {
        static AddInId m_appId;
        static UpdaterId m_updaterId;


        /// <summary>
        /// Finds the failure definition id based on a constant GUID
        /// </summary>
        /// <returns></returns>
        private static FailureDefinitionId GetFailureDefinitionId()
        {
            FailureDefinitionRegistry failureDefinitionRegistry = Autodesk.Revit.ApplicationServices.Application.GetFailureDefinitionRegistry();
            FailureDefinitionId FailureDefinitionId = new FailureDefinitionId(FailureDefinitionIdGuid.Value);
            return failureDefinitionRegistry.FindFailureDefinition(FailureDefinitionId).GetId();
        }

        public FailureDefinitionId _failureId = GetFailureDefinitionId();

        // constructor takes the AddInId for the add-in associated with this updater
        public SplitElementUpdater(AddInId id)
        {
            m_appId = id;
            m_updaterId = new UpdaterId(m_appId, Guid.NewGuid());
        }

        public void Execute(UpdaterData data)
        {
            try
            {
                Document doc = data.GetDocument();
                ICollection<ElementId> changedElements = data.GetModifiedElementIds().Concat(data.GetDeletedElementIds()).ToList();

                DialogResult userResult = MessageBox.Show(
                    "You are trying to edit a part which was divided by an automation tool. To edit this part, first revert the division, then edit the part normally. Do you wish to open the “Surface Split” tool?",
                    "", MessageBoxButtons.OKCancel);
                if (userResult.Equals(DialogResult.OK))
                {
                    RevitDBUtils.InitializeStaticUtils(doc, RevitDBUtils.uidoc, RevitDBUtils.uiapp, RevitDBUtils.dllFolder, eDiscipline.Architectural);
                    RevitDBUtils.ExecuteMethodInEvent(() =>
                    {
                        SurfaceSplitTabsWindow window = new SurfaceSplitTabsWindow(1);
                        window.Show();
                    }, "Open Revert Surface Split window");
                }
                // Create a failure message that will cancel the operation
                FailureMessage failureMessage = new FailureMessage(_failureId);

                failureMessage.SetFailingElements(changedElements);

                doc.PostFailure(failureMessage);
            }
            catch
            {
            }
        }

        public string GetAdditionalInformation()
        {
            return "Surface Split Updater for preventing modifying elements divided by Surface Split";
        }

        public ChangePriority GetChangePriority()
        {
            return ChangePriority.FreeStandingComponents;
        }

        public UpdaterId GetUpdaterId()
        {
            return m_updaterId;
        }

        public string GetUpdaterName()
        {
            return "Surface Split Updater";
        }
    }

 

This class works as expected and blocks the deletion, I'm setting up the trigger here:

public static UpdaterId SurfaceSplitElementUpdaterSetup(AddInId addinId, Document doc)
        {
            SplitElementUpdater splitUpdater = new SplitElementUpdater(addinId);//Create a surface split updater for alerting on modified divided elements

            UpdaterRegistry.RegisterUpdater(splitUpdater);//register the updater
            UpdaterRegistered = true;
            // Creating filters for the updater:

            ElementMulticategoryFilter catFilter = new ElementMulticategoryFilter(new List<BuiltInCategory> { BuiltInCategory.OST_Parts });//Create categories filter

            ExtensibleStorageFilter extensibleStorageFilter = new ExtensibleStorageFilter(SplitFlag.GetGuid());
            //Create extensible storage filter of elements with the Surface split Element Info Guid

            LogicalAndFilter bothFilters = new LogicalAndFilter(catFilter, extensibleStorageFilter);//combine both filters to a single filter

            ChangeType elementDeletion = Element.GetChangeTypeElementDeletion();//the change type of an element deletion
            ChangeType geometryChange = Element.GetChangeTypeGeometry();//the change type of a geometry change

            UpdaterId updaterId = splitUpdater.GetUpdaterId();

            //We want to trigger when changing some of the params:
            List<Parameter> parameters = new List<Parameter>();
            parameters.Add(GetPanelIDUtils.GetPanelIdParameter(doc));
            parameters.Add(GetFromDocUtils.GetParameter(ParametersConstants.FACTORY, doc));
            parameters.Add(GetFromDocUtils.GetParameter(ParametersConstants.MATERIAL, doc));
            parameters.Add(GetFromDocUtils.GetParameter(BuiltInParameter.ROOF_BASE_LEVEL_PARAM, doc));

            foreach (Parameter parameter in parameters)
            {
                if (parameter != null)
                {
                    ChangeType paramChange = Element.GetChangeTypeParameter(parameter);
                    UpdaterRegistry.AddTrigger(updaterId, bothFilters, paramChange);
                }
            }

            // add the triggers for the updater
            
            UpdaterRegistry.AddTrigger(updaterId, bothFilters, elementDeletion);
            UpdaterRegistry.AddTrigger(updaterId, bothFilters, geometryChange);

            return splitUpdater.GetUpdaterId();
        }

All of this is fine!

 

And now, the problem 🙂

We know we can't swallow the error because it's of 'error' severity, but I'm unable to resolve it as well (Tried with / without resolve failure, tried proceed with commit and continue, nothing worked)

public FailureProcessingResult PreprocessFailures(FailuresAccessor failuresAccessor)
        {
            IList<FailureMessageAccessor> failList = new List<FailureMessageAccessor>();
            failList = failuresAccessor.GetFailureMessages(); // Inside event handler, get all warnings

            foreach (FailureMessageAccessor failure in failList)
            {
                FailureDefinitionId failID = failure.GetFailureDefinitionId();
                if (failID == BuiltInFailures.DPartFailures.DeletingDPartWillDeleteMorePartsError)
                {
                    failure.SetCurrentResolutionType(FailureResolutionType.Default);
                    failuresAccessor.GetFailureHandlingOptions().SetClearAfterRollback(true);
                    //failuresAccessor.ResolveFailure(failure);
                    TransactionStatus a = failuresAccessor.RollBackPendingTransaction();
                    return FailureProcessingResult.ProceedWithRollBack;
                }
            }

            return FailureProcessingResult.Continue;
        }

 

Thank you!

0 Likes

aignatovich
Advisor
Advisor
Accepted solution

Try something like that:

 

public class RevitApplication : IExternalApplication
{
	private SyntheticFailureReplacement failureReplacement;

	public Result OnStartup(UIControlledApplication application)
	{
		failureReplacement = new SyntheticFailureReplacement();

		application.ControlledApplication.FailuresProcessing += ControlledApplicationOnFailuresProcessing;

		return Result.Succeeded;
	}

	public Result OnShutdown(UIControlledApplication application)
	{
		application.ControlledApplication.FailuresProcessing -= ControlledApplicationOnFailuresProcessing;

		return Result.Succeeded;
	}

	private void ControlledApplicationOnFailuresProcessing(object sender, FailuresProcessingEventArgs e)
	{
		var failuresAccessor = e.GetFailuresAccessor();

		var failureMessages = failuresAccessor
			.GetFailureMessages(FailureSeverity.Error)
			.Where(x => x.GetFailureDefinitionId() == BuiltInFailures.DPartFailures.DeletingDPartWillDeleteMorePartsError)
			.ToList();
		
		if (failureMessages.Any())
		{
			var failureHandlingOptions = failuresAccessor.GetFailureHandlingOptions();

			failureHandlingOptions.SetClearAfterRollback(true);

			failuresAccessor.SetFailureHandlingOptions(failureHandlingOptions);

			e.SetProcessingResult(FailureProcessingResult.ProceedWithRollBack);

			failureReplacement.PostFailure(failureMessages.SelectMany(x => x.GetFailingElementIds()));
		}
	}
}

public class SyntheticFailureReplacement : IExternalEventHandler
{
	private readonly ExternalEvent externalEvent;
	private readonly List<ElementId> failingElementIds = new List<ElementId>();
	private readonly FailureDefinitionId failureDefinitionId = new FailureDefinitionId(new Guid("bc0dc2ef-d928-42e4-9c9b-521cb822d3fd"));

	public SyntheticFailureReplacement()
	{
		externalEvent = ExternalEvent.Create(this);

		FailureDefinition.CreateFailureDefinition(failureDefinitionId, FailureSeverity.Warning, "My accurate message replacement");
	}

	public void PostFailure(IEnumerable<ElementId> failingElements)
	{
		failingElementIds.Clear();
		failingElementIds.AddRange(failingElements);

		externalEvent.Raise();
	}

	public void Execute(UIApplication app)
	{
		var document = app.ActiveUIDocument.Document;

		using (var transaction = new Transaction(document, "auxiliary transaction"))
		{
			var failureHandlingOptions = transaction.GetFailureHandlingOptions();
			failureHandlingOptions.SetForcedModalHandling(false);

			transaction.SetFailureHandlingOptions(failureHandlingOptions);
			
			transaction.Start();

			var failureMessage = new FailureMessage(failureDefinitionId);

			failureMessage.SetFailingElements(failingElementIds);
			
			document.PostFailure(failureMessage);

			transaction.Commit();
		}
	}

	public string GetName() => nameof(SyntheticFailureReplacement);
}

tomerFUNPJ
Enthusiast
Enthusiast
Worked like a charm. Thanks!
0 Likes

jeremy_tammik
Autodesk
Autodesk

Thank you very much, @aignatovich , for the solution and all the patient explanations! In the hope that this will prove useful to many others in the future, preserved for posterity on the blog:

  

https://thebuildingcoder.typepad.com/blog/2022/05/revit-2023-api-docs-and-disabling-an-error-failure...

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open

prasanna_murumkar
Autodesk
Autodesk

Hi,
Tried above code in my sample example  of  external command .
Not able to supress error like 
"Line is too short".Any Idea?
And i have some more errors like cant unjoin element while moving elements.
Is anyone can give sample example to supress similar kind of error.
Thanks in advance.

0 Likes

jeremy_tammik
Autodesk
Autodesk

The "Line is too short" error cannot be suppressed, because the line is too short, so that its coordinates cannot be stored adequately in the BIM. You have to cancel and abort the operation that is (trying to) cause these lines.

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes

prasanna_murumkar
Autodesk
Autodesk

Ok. Thanks.

Here is one way to got to supress that message by  using
f.SetCurrentResolutionType(FailureResolutionType.DeleteElements);.

private void OnFailuresProcessing(object sender, FailuresProcessingEventArgs e)
{

var failureAccessor = e.GetFailuresAccessor();

// Ignore all warnings.
failureAccessor.DeleteAllWarnings();

// Resolve all resolvable errors.
var failures = failureAccessor.GetFailureMessages();
if (!failures.Any())
{
return;
}
IList<FailureMessageAccessor> failureMessages
= failureAccessor.GetFailureMessages();

foreach (FailureMessageAccessor f in failures)
{
var lst= f.GetFailingElementIds();
FailureSeverity failureSeverity
= f.GetSeverity();
if (failureSeverity == FailureSeverity.Warning)
{

}
else
{
FailureResolutionType fs;
if (true)
{
try
{
string value= f.GetDescriptionText();
FailureResolutionType failureResolutionType= f.GetCurrentResolutionType();
f.SetCurrentResolutionType(FailureResolutionType.DeleteElements);
}
catch
{
}

failureAccessor.ResolveFailures(failures);
FailureProcessingResult fpr = FailureProcessingResult.ProceedWithCommit;
e.SetProcessingResult(fpr);

}



0 Likes

jeremy_tammik
Autodesk
Autodesk

Thank you for sharing the solution. I just noticed your internal discussion with the development team. Their concrete suggestion might be helpful to share as well:

  

Answer: Did you set the FailureProcessingResult back to the arguments object with FailuresProcessingEventArgs.SetProcessingResult() method? The code seems to end before this step. The documentation mentions that if this is not done the failures are not cleared.

  

Response: No. My code goes up till this step only. OK, I will add e.SetProcessingResult(fpr). It worked for one model. I am trying now for other model with issues like unjoin element etc. It worked on the other model as well.

  

Thank you!

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open