Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

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

19 REPLIES 19
SOLVED
Reply
Message 1 of 20
dantartaglia8696
4872 Views, 19 Replies

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

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

 

19 REPLIES 19
Message 2 of 20

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

Message 3 of 20

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;

Message 4 of 20

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

Moustafa Khalil
Cropped-Sharp-Bim-500x125-Autodesk-1
Message 5 of 20

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.

 

Message 6 of 20

Here is a warning showing the constraint error elements

Message 7 of 20
arautio
in reply to: dantartaglia8696

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;
        }
    }  

Message 8 of 20
dantartaglia8696
in reply to: arautio

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

Message 9 of 20

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
Message 10 of 20

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.

Message 11 of 20
arautio
in reply to: dantartaglia8696

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;
        }
    }  

 

Message 12 of 20
dantartaglia8696
in reply to: arautio

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.)'. 

Message 13 of 20
FAIR59
in reply to: dantartaglia8696

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;
        }
}
Message 14 of 20
dantartaglia8696
in reply to: FAIR59

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

Message 15 of 20

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

Message 16 of 20
Sean_Page
in reply to: FAIR59

@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
Message 17 of 20
FAIR59
in reply to: Sean_Page

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

Message 18 of 20
RPTHOMAS108
in reply to: Sean_Page

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).

 

Message 19 of 20
Sean_Page
in reply to: RPTHOMAS108

@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
Message 20 of 20
Sean_Page
in reply to: Sean_Page

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

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report