How to catch and close the warning: Constraints are not satisfied?

dantartaglia8696
Advocate
Advocate

How to catch and close the warning: Constraints are not satisfied?

dantartaglia8696
Advocate
Advocate

Hi,

While mining data from a Revit model I'm using: Document.EditFamily() to get the RFA's and then their file size. Some families are causing this error and need manual input to continue the process. I tried Application.FailuresProcessing and Application.DialogBoxShowing but they do not seem to catch this warning to allow me to dismiss the message. I'm not saving the model after the process (the constraint issue won't matter) but want to automate the process without having to monitor. I apologize if this was asked before, didn't find it in the search.

 

Thanks,

Dan

 

0 Likes
Reply
Accepted solutions (1)
5,049 Views
19 Replies
Replies (19)

naveen.kumar.t
Autodesk Support
Autodesk Support

Hi @dantartaglia8696 ,

try using the below link

https://thebuildingcoder.typepad.com/blog/2016/09/warning-swallower-and-roomedit3d-viewer-extension.... 

https://thebuildingcoder.typepad.com/blog/2014/05/on-handling-warnings-and-failures.html 

 

It will help you to catch and delete the warnings.


Naveen Kumar T
Developer Technical Services
Autodesk Developer Network

dantartaglia8696
Advocate
Advocate

Hi @naveen.kumar.t I'm not using any transactions in my code, Document.EditFamily does not need one to be defined. Doesn't seem like you can. Even tried initiating like an event (as a test) but it does not go into the failure logic.

 

Autodesk.Revit.ApplicationServices.Application app = commandData.Application.Application;
app.FailuresProcessing += FaliureProcessor;

0 Likes

Moustafa_K
Collaborator
Collaborator

hi, Can you send this family model, to be able to reproduce this error

Moustafa Khalil
Cropped-Sharp-Bim-500x125-Autodesk-1
0 Likes

dantartaglia8696
Advocate
Advocate

Hi @Moustafa_K , I will try and get back to you but if you have a relatively large model, and create a simple add-in using that method to save the RFA, you will most likely see the error.

 

0 Likes

dantartaglia8696
Advocate
Advocate

Here is a warning showing the constraint error elements

0 Likes

arautio
Advocate
Advocate

I had to deal with one of those error dialogues on a solid cutting routine, found a way to catch it and send a message to the user to adjust geometry and recut.

 

using (Autodesk.Revit.DB.Transaction trans = new Autodesk.Revit.DB.Transaction(document))
        {
            trans.Start("CutVoid");
                FailureHandlingOptions options = trans.GetFailureHandlingOptions();
                MyPreProcessor preproccessor = new MyPreProcessor();
                options.SetClearAfterRollback(true);
                options.SetFailuresPreprocessor(preproccessor);
                trans.SetFailureHandlingOptions(options);

                try
                {
                    InstanceVoidCutUtils.AddInstanceVoidCut(document, grating, gratingvoid);
                }
                catch
                {
                    TaskDialog.Show("Revit""Can Not Cut");
                }
                    
            trans.Commit();
        }  

 

It needs this class as well

 

 public class MyPreProcessor: IFailuresPreprocessor
    {
        FailureProcessingResult IFailuresPreprocessor.PreprocessFailures(FailuresAccessor failuresAccessor)
        {
            String transactionName = failuresAccessor.GetTransactionName();

            IList<FailureMessageAccessor> fmas = failuresAccessor.GetFailureMessages();


            if (fmas.Count == 0)
                return FailureProcessingResult.Continue;

            
            if (transactionName.Equals("CutVoid"))
            {
                foreach (FailureMessageAccessor fma in fmas)
                {
                    if (fma.GetSeverity() == FailureSeverity.Error)
                    {
                        TaskDialog.Show("Revit""Failed to Cut, Move grating a little and Cut again");
                        failuresAccessor.DeleteAllWarnings();
                        return FailureProcessingResult.ProceedWithRollBack;
                    }
                    else
                    {
                        TaskDialog.Show("Revit""Failed to Cut, Move grating a little and Cut again");
                        failuresAccessor.DeleteWarning(fma);
                    }

                }
            }
            else
            {
                foreach (FailureMessageAccessor fma in fmas)
                {
                    TaskDialog.Show("Revit""Failed to Cut, Move grating a little and Cut again");
                    failuresAccessor.DeleteAllWarnings();
                }
            }
            return FailureProcessingResult.Continue;
        }
    }  

0 Likes

dantartaglia8696
Advocate
Advocate

Hi  @arautio, this is an automated process that does not have a transaction. I'm not sure how your solution can help.

0 Likes

Moustafa_K
Collaborator
Collaborator

Honestly, my team used to face this scenario, when there was a dimension between a local element and a linked element. When the linked element deleted from the Linked File, the references are lost, which trigger this warning message upon opening your project.

Another possible scenario I faced, is the family itself is updated/Changed in the central Model and the local file not uptodate... or corrupted in the local file which cross reference with central Model (which might raise this issue as well. 

all in all there are many reasons for this warning.

 

I would say try creating a new project and copy this family instance to the new project from the current project. Then save and reopen the new project file, if the problem persist, send me the new project, I can show some help, and I will try my own findings. If it works fine then most likely you need to audit your current project.

 

I hope this drops some lights to solve your problem

Moustafa Khalil
Cropped-Sharp-Bim-500x125-Autodesk-1
0 Likes

dantartaglia8696
Advocate
Advocate

Hi @Moustafa_K I'm batch processing multiple models from multiple projects. Any solution would have to be automatic without fixing any constraint issues first.

0 Likes

arautio
Advocate
Advocate

Can you add a transaction at the end,  just to handle the error?  The only way I know how to get through those kind of error dialogues is with the transaction system I posted above.  Just change the things in orange to match your code.     You should be able to use a trans in your automated process, just keep the error messages remarked out and no dialogues will appear.

 

using (Autodesk.Revit.DB.Transaction trans = new Autodesk.Revit.DB.Transaction(document))
        {
            trans.Start("Whatever");    // make sure the name for this transaction is used in the MyPreProcessor
                FailureHandlingOptions options = trans.GetFailureHandlingOptions();
                MyPreProcessor preproccessor = new MyPreProcessor();
                options.SetClearAfterRollback(true);
                options.SetFailuresPreprocessor(preproccessor);
                trans.SetFailureHandlingOptions(options);
                try
                {
                    // Put the code that will or may invoke the error box here.
                }
                catch
                {
                    // whatever you want here, or nothing
                }   
            trans.Commit();
        }  

 

public class MyPreProcessor: IFailuresPreprocessor
    {
        FailureProcessingResult IFailuresPreprocessor.PreprocessFailures(FailuresAccessor failuresAccessor)
        {
            String transactionName = failuresAccessor.GetTransactionName();
            IList<FailureMessageAccessor> fmas = failuresAccessor.GetFailureMessages();
            if (fmas.Count == 0)
                return FailureProcessingResult.Continue;
            if (transactionName.Equals("Whatever - match transaction name above"))
            {
                foreach (FailureMessageAccessor fma in fmas)
                {
                    if (fma.GetSeverity() == FailureSeverity.Error)
                    {
                        //TaskDialog.Show("Revit""Error message if you want one or just remark out");
                        failuresAccessor.DeleteAllWarnings();
                        return FailureProcessingResult.ProceedWithRollBack;
                    }
                    else
                    {
                        //TaskDialog.Show("Revit""Error message if you want one or just remark out");
                        failuresAccessor.DeleteWarning(fma);
                    }
                }
            }
            else
            {
                foreach (FailureMessageAccessor fma in fmas)
                {
                    //TaskDialog.Show("Revit""Error message if you want one or just remark out");
                    failuresAccessor.DeleteAllWarnings();
                }
            }
            return FailureProcessingResult.Continue;
        }
    }  

 

0 Likes

dantartaglia8696
Advocate
Advocate

I'll give it a try but I doubt it will work. According to the help file, an exception will be thrown if .EditFamily 'is called while the document is modifiable (i.e. it has an unfinished transaction.)'. 

0 Likes

FAIR59
Advisor
Advisor
Accepted solution

If there is no pure API solution, you can "drive"the dialog using UIAutomation

Add the following references to your rpoject:

UIAutomationClient,   UIAutomationClientsideProviders, UIAutomationProvider and UIAutomationTypes.

 

You can track changes of focus by subscribing to the AutomationFocusChangedEvent. This is a systemwide event, so even changes to other processes will be reported. Once you find the (focussed) OK-Button of the dialog, you can "press" it  programmaticly.

    //using System.Windows.Automation;
    public class Main_Proc : IExternalApplication
    {
        private AutomationFocusChangedEventHandler focusHandler = null;
        private int thisProcessId;
        public Autodesk.Revit.UI.Result OnStartup(UIControlledApplication application)
        {
            thisProcessId = System.Diagnostics.Process.GetCurrentProcess().Id;
            focusHandler = new AutomationFocusChangedEventHandler(OnFocusChange);
            Automation.AddAutomationFocusChangedEventHandler(focusHandler);
            return Result.Succeeded;
        }

        /// <summary>
        /// Handle the event.
        /// </summary>
        /// <param name="src">Object that raised the event.</param>
        /// <param name="e">Event arguments.</param>
        private void OnFocusChange(object src, AutomationFocusChangedEventArgs e)
        {
            AutomationElement source = src as AutomationElement;
            if (source == null) return;
            if (source.Current.ProcessId != thisProcessId) return; // the eventhandler reacts to systemwide events => check for current process.
            if (source.Current.Name != "OK") return; // the OK-button of the dialog has focus initially. the string might be language dependent 
            // find the dialog-form
            TreeWalker walker = TreeWalker.ControlViewWalker;
            AutomationElement parent = walker.GetParent(source);
            if (parent == null) return;
            if (parent.Current.ClassName != "#32770") return; // the dialog-form has a specific class, value for Revit 2019, might be different for other versions
            if (parent.Current.Name != "Autodesk Revit 2019") return; // the dialog-form has a specific Caption, might be language dependent, is version dependent
            // right button on the right dialog-form => "click" button
            Object invPattern;
            if (source.TryGetCurrentPattern(InvokePattern.Pattern, out invPattern))
            {
                (invPattern as InvokePattern).Invoke();
            }
        }
        public Autodesk.Revit.UI.Result OnShutdown(UIControlledApplication application)
        {
            if (focusHandler != null)
            {
                Automation.RemoveAutomationFocusChangedEventHandler(focusHandler);
            }
            return Result.Succeeded;
        }
}

dantartaglia8696
Advocate
Advocate

That worked, only issue is that it clicks the default button which is Cancel. Only a couple of families cause this error so it's not a bug deal for me. Thanks for your help everyone!

 

Dan

0 Likes

buihaviet
Participant
Participant

Thanks @FAIR59 ,

 

And for anybody don't want to click only cancel button, I have updated the code a little to click a specific button on the dialog.

            var thisProcessId = System.Diagnostics.Process.GetCurrentProcess().Id;

            AutomationElement source = src as AutomationElement;
            if (source != null)
            {
                try
                {
                    if (source.Current.ProcessId != thisProcessId) return; // the eventhandler reacts to systemwide events => check for current process.
                }
                catch
                {
                    return;
                }
                // find the dialog-form
                TreeWalker walker = TreeWalker.ControlViewWalker;
                AutomationElement parent = walker.GetParent(source);
                if (parent != null && parent.Current.Name == $"Autodesk Revit {2019}")
                {
                    AutomationElement element = walker.GetFirstChild(parent);
                    do
                    {

                        if (element.Current.Name == "Remove Constraints" ||
                            element.Current.Name == "Delete Element(s)")
                        {
                            break;
                        }
                        element = walker.GetNextSibling(element);

                    } while (element != null);

                    if (element != null)
                    {
                        if (element.TryGetCurrentPattern(InvokePattern.Pattern, out Object invPattern))
                        {
                            (invPattern as InvokePattern).Invoke();
                        }
                    }
                }
            }

 

Cheer,

Viet Bui

Sean_Page
Collaborator
Collaborator

@FAIR59 would you be able to identify where one would find the "ClassName" you used? I would like to leverage this, but want to make sure I have it correct for 2021.

 

Thank you!

Sean Page, AIA, NCARB, LEED AP
Partner, Computational Designer, Architect
0 Likes

FAIR59
Advisor
Advisor

If my memory serves me right, I used  the WinSpy program to find the properties of the Controls. 

0 Likes

RPTHOMAS108
Mentor
Mentor

Surprised this can't be handled via failures framework looking through RevitAPI.xml there are three failures with this exact wording. There are others similar for equality dimensions but these only without that.

 

Constraints are not satisfied:
Autodesk.Revit.DB.BuiltInFailures.DimensionFailures.RegenConstraintUnsatisfiedWarn
Autodesk.Revit.DB.BuiltInFailures.DimensionFailures.RegenConstraintUnsatisfied
Autodesk.Revit.DB.BuiltInFailures.ArrayFailures.ConstraintsNotSatisfied

 

Not sure about severity of these: one looks like 'Warning' from the wording (can be dismissed). The one noted in dialogue box message is 'Error' (that can't be dismissed and needs a resolution).  If it is 'Warning' it usually just has ok button.

 

'Error' failures can be resolved by applying a default resolution i.e. delete constraints:

See FailuresAccessor.ResolveFailure (considering also what is written about ProceedWithCommit).

 

Sean_Page
Collaborator
Collaborator

@RPTHOMAS108 thanks for the follow up on this, and I appreciate any help you have to offer. I think I am like 99% of the way there, the only hurdle I can't seem to get over is the display of the error to the user. Since this error is thrown on the opening of the document I don't have the Transaction to attach a PreProcessor onto. I have tested the code below and I get the correct resolution and it is applied, but it doesn't "clear" the message as the text with the FailureAccessor.ResolveFailure() indicates so the dialog is still displayed. 

 

spage_0-1613139389629.png

 

Is there something I am missing that would let me snag this BEFORE now and/or use the IFailureProcessor?

private void ControlledApplication_FailuresProcessing(object sender, FailuresProcessingEventArgs e)
		{
			FailuresAccessor fas = e.GetFailuresAccessor();
			fas.DeleteAllWarnings();

			List<FailureMessageAccessor> fma = fas.GetFailureMessages().ToList();
			List<ElementId> del = new List<ElementId>();
			
			foreach(FailureMessageAccessor fa in fma)
			{
				try
				{
					if(fa.GetSeverity() != FailureSeverity.Warning)
					{
						if(fa.GetDefaultResolutionCaption() == "Remove Constraints")
						{
							if(fas.IsFailureResolutionPermitted(fa, FailureResolutionType.UnlockConstraints))
							{
								fa.SetCurrentResolutionType(FailureResolutionType.UnlockConstraints);
								fas.ResolveFailure(fa);
							}
							else
							{
								fa.SetCurrentResolutionType(FailureResolutionType.DeleteElements);
								fas.ResolveFailure(fa);
							}
							continue;
						}
					}
				}
				catch
				{
					continue;
				}
			}
		}

  

Sean Page, AIA, NCARB, LEED AP
Partner, Computational Designer, Architect
0 Likes

Sean_Page
Collaborator
Collaborator

I was VERY close, in fact I was able to fine the SINGLE line I needed here: 

https://thebuildingcoder.typepad.com/blog/2010/11/failure-api-take-two.html 

 

That line is what allows you to return the action to the transaction since you didn't initiate it.

e.SetProcessingResult(FailureProcessingResult.ProceedWithCommit);

 

Here is the code that I have used to handle these errors as well as all warnings. Keep in mind I am automating the opening, mining, and then saving the models as something else so deleting the warnings in this matter doesn't have any impact on my models. I would caution anyone else to use this exactly as it is because it could have unintended consequences.

private void ControlledApplication_FailuresProcessing(object sender, FailuresProcessingEventArgs e)
		{
			FailuresAccessor fas = e.GetFailuresAccessor();
			//Delete all failures of Warning Severity
			fas.DeleteAllWarnings();
			
			List<FailureMessageAccessor> fma = fas.GetFailureMessages().ToList();
			List<ElementId> del = new List<ElementId>();

			foreach(FailureMessageAccessor fa in fma)
			{
				try
				{
					if(fa.GetSeverity() != FailureSeverity.Warning)
					{
						if(fa.GetDefaultResolutionCaption() == "Remove Constraints")
						{
							if(fas.IsFailureResolutionPermitted(fa, FailureResolutionType.UnlockConstraints))
							{
								fa.SetCurrentResolutionType(FailureResolutionType.UnlockConstraints);
								fas.ResolveFailure(fa);
							}
							else
							{
								fa.SetCurrentResolutionType(FailureResolutionType.DeleteElements);
								fas.ResolveFailure(fa);
							}
							//This line is CRITICAL to return and continue after setting the Resolution.
							e.SetProcessingResult(FailureProcessingResult.ProceedWithCommit);
							continue;
						}
					}

					foreach(ElementId f in fa.GetAdditionalElementIds())
					{
						if(!del.Contains(f))
						{
							del.Add(f);
						}
					}
					fas.DeleteWarning(fa);

					if(del.Count > 0)
					{
						fas.DeleteElements(del);
					}
				}
				catch(Exception ex)
				{
					continue;
				}
			}
		}
 

 

Sean Page, AIA, NCARB, LEED AP
Partner, Computational Designer, Architect