Salome HOME
Debug of movement of part results
[modules/shaper.git] / src / Model / Model_Update.cpp
index e3e9f548b74d5c56d651992a5f8b20497f70d861..561882b7c28d3157111c3cff3df98d6bbb186620 100644 (file)
@@ -33,6 +33,7 @@
 using namespace std;
 
 Model_Update MY_UPDATER_INSTANCE;  /// the only one instance initialized on load of the library
+//#define DEB_UPDATE
 
 Model_Update::Model_Update()
 {
@@ -59,6 +60,7 @@ Model_Update::Model_Update()
   myIsAutomatic =
     Config_PropManager::findProp("Model update", "automatic_rebuild")->value() == "true";
   myIsParamUpdated = false;
+  myIsFinish = false;
 }
 
 void Model_Update::processEvent(const std::shared_ptr<Events_Message>& theMessage)
@@ -72,7 +74,6 @@ void Model_Update::processEvent(const std::shared_ptr<Events_Message>& theMessag
   static const Events_ID kOpFinishEvent = aLoop->eventByName("FinishOperation");
   static const Events_ID kOpAbortEvent = aLoop->eventByName("AbortOperation");
   static const Events_ID kOpStartEvent = aLoop->eventByName("StartOperation");
-  bool isOperationChanged = false;
   if (theMessage->eventID() == kChangedEvent) { // automatic and manual rebuild flag is changed
     bool aPropVal =
       Config_PropManager::findProp("Model update", "automatic_rebuild")->value() == "true";
@@ -100,38 +101,37 @@ void Model_Update::processEvent(const std::shared_ptr<Events_Message>& theMessag
       // created objects are always must be up to date (python box feature)
       // and updated not in internal uptation chain
       myJustUpdated.insert(*anObjIter);
+      #ifdef DEB_UPDATE
+      if ((*anObjIter)->data() && (*anObjIter)->data()->isValid()) {
+        std::cout<<"Add updated "<<(*anObjIter)->groupName()<<" "
+          <<(*anObjIter)->data()->name()<<std::endl;
+      }
+      #endif
     }
-    // this event is for solver update, not here, do not react immideately
+    // this event is for solver update, not here, do not react immediately
     if (!isOnlyResults && !(theMessage->eventID() == kMovedEvent))
       processOperation(false);
-  } else if (theMessage->eventID() == kOpStartEvent) {
-    // we don't need the update only on operation start (caused problems in PartSet_Listener::processEvent)
-    isOperationChanged = true;
-  } else if (theMessage->eventID() == kOpFinishEvent || theMessage->eventID() == kOpAbortEvent) {
-    processOperation(true, theMessage->eventID() == kOpFinishEvent);
-    isOperationChanged = true;
-  }
-  if (isOperationChanged) {
-    // remove all macros before clearing all created and execute all not-previewed
-    std::set<ObjectPtr>::iterator anUpdatedIter = myJustUpdated.begin();
-    while(anUpdatedIter != myJustUpdated.end()) {
+  } else if (theMessage->eventID() == kOpFinishEvent || theMessage->eventID() == kOpAbortEvent ||
+      theMessage->eventID() == kOpStartEvent) {
+
+    if (!(theMessage->eventID() == kOpStartEvent)) {
+      myIsFinish = true;
+      processOperation(true, theMessage->eventID() == kOpFinishEvent);
+      myIsFinish = false;
+    }
+    // remove all macros before clearing all created
+    std::set<ObjectPtr>::iterator anUpdatedIter = myWaitForFinish.begin();
+    while(anUpdatedIter != myWaitForFinish.end()) {
       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anUpdatedIter);
       if (aFeature.get()) {
-        // execute not-previewed feature on "apply"
-        if (!aFeature->isPreviewNeeded()) {
-          static ModelAPI_ValidatorsFactory* aFactory = ModelAPI_Session::get()->validators();
-          if (aFactory->validate(aFeature)) {
-            executeFeature(aFeature);
-          }
-        }
-        // remove macro on apply
+        // remove macro on finish
         if (aFeature->isMacro()) {
           aFeature->document()->removeFeature(aFeature);
-          myJustUpdated.erase(aFeature);
+          myWaitForFinish.erase(aFeature);
         }
         // to avoid the map update problems on "remove"
-        if (myJustUpdated.find(aFeature) == myJustUpdated.end()) {
-          anUpdatedIter = myJustUpdated.begin();
+        if (myWaitForFinish.find(aFeature) == myWaitForFinish.end()) {
+          anUpdatedIter = myWaitForFinish.begin();
         } else {
           anUpdatedIter++;
         }
@@ -139,7 +139,11 @@ void Model_Update::processEvent(const std::shared_ptr<Events_Message>& theMessag
         anUpdatedIter++;
       }
     }
+    // in the end of transaction everything is updated, so clear the old objects (the only one
+    // place where results are cleared)
     myIsParamUpdated = false;
+    myJustUpdated.clear();
+    myWaitForFinish.clear();
   }
 }
 
@@ -148,7 +152,7 @@ void Model_Update::processOperation(const bool theTotalUpdate, const bool theFin
   if (theFinish) {
     // the hardcode (DBC asked): hide the sketch referenced by extrusion on apply
     std::set<std::shared_ptr<ModelAPI_Object> >::iterator aFIter;
-    for(aFIter = myJustUpdated.begin(); aFIter != myJustUpdated.end(); aFIter++)
+    for(aFIter = myWaitForFinish.begin(); aFIter != myWaitForFinish.end(); aFIter++)
     {
       FeaturePtr aF = std::dynamic_pointer_cast<ModelAPI_Feature>(*aFIter);
       if (aF && aF->data()->isValid() && aF->getKind() == "Extrusion") {
@@ -166,6 +170,9 @@ void Model_Update::processOperation(const bool theTotalUpdate, const bool theFin
   }
   // perform update of everything if needed
   if (!myIsExecuted) {
+    #ifdef DEB_UPDATE
+      std::cout<<"****** Start processing"<<std::endl;
+    #endif
     myIsExecuted = true;
 
     bool isAutomaticChanged = false;
@@ -179,10 +186,17 @@ void Model_Update::processOperation(const bool theTotalUpdate, const bool theFin
     DocumentPtr aRootDoc = ModelAPI_Session::get()->moduleDocument();
     Model_Objects* anObjs = std::dynamic_pointer_cast<Model_Document>(aRootDoc)->objects();
     if (!anObjs) return;
+    // two cycles: parameters are first to process
     FeaturePtr aFeatureIter = anObjs->firstFeature();
     std::set<FeaturePtr> aProcessedFeatures; // to avoid processing twice
     for (; aFeatureIter.get(); aFeatureIter = anObjs->nextFeature(aFeatureIter)) {
-      updateFeature(aFeatureIter, aProcessedFeatures);
+      if (aFeatureIter->getKind() == "Parameter")
+        updateFeature(aFeatureIter, aProcessedFeatures);
+    }
+    aFeatureIter = anObjs->firstFeature();
+    for (; aFeatureIter.get(); aFeatureIter = anObjs->nextFeature(aFeatureIter)) {
+      if (aFeatureIter->getKind() != "Parameter")
+        updateFeature(aFeatureIter, aProcessedFeatures);
     }
 
     if (isAutomaticChanged) myIsAutomatic = false;
@@ -192,6 +206,9 @@ void Model_Update::processOperation(const bool theTotalUpdate, const bool theFin
     static Events_Loop* aLoop = Events_Loop::loop();
     static Events_ID EVENT_DISP = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
     aLoop->flush(EVENT_DISP);
+    #ifdef DEB_UPDATE
+      std::cout<<"****** End processing"<<std::endl;
+    #endif
   }
 }
 
@@ -206,6 +223,9 @@ void Model_Update::updateFeature(FeaturePtr theFeature, std::set<FeaturePtr>& th
   if (theFeature->isDisabled())
     return;
 
+  #ifdef DEB_UPDATE
+    std::cout<<"Update Feature "<<theFeature->name()<<std::endl;
+  #endif
   CompositeFeaturePtr aCompos = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theFeature);
   // If automatice update is not needed and feature attributes were not updated right now,
   // do not execute it and do not update arguments.
@@ -222,10 +242,17 @@ void Model_Update::updateFeature(FeaturePtr theFeature, std::set<FeaturePtr>& th
 
   // composite feature must be executed after sub-features execution
   if (aCompos) {
+    // two cycles: parameters must be processed first
+    for(int a = 0; a < aCompos->numberOfSubs(); a++) {
+      FeaturePtr aSub = aCompos->subFeature(a);
+      if (aSub->getKind() == "Parameter")
+        updateFeature(aSub, theProcessed);
+    }
     // number of subs can be changed in execution: like fillet
     for(int a = 0; a < aCompos->numberOfSubs(); a++) {
       FeaturePtr aSub = aCompos->subFeature(a);
-      updateFeature(aSub, theProcessed);
+      if (aSub->getKind() != "Parameter")
+       updateFeature(aSub, theProcessed);
     }
   }
   // this checking must be after the composite feature sub-elements processing:
@@ -241,21 +268,46 @@ void Model_Update::updateFeature(FeaturePtr theFeature, std::set<FeaturePtr>& th
   if (myIsAutomatic && theFeature->data()->execState() == ModelAPI_StateMustBeUpdated)
     aJustUpdated = true;
 
+  // On abort, undo or redo execute is not needed: results in document are updated automatically
+  // But redisplay is needed: results are updated, must be also updated in the viewer.
+  if (aJustUpdated && 
+      !std::dynamic_pointer_cast<Model_Document>(theFeature->document())->executeFeatures()) {
+    if (!theFeature->isPersistentResult()) { // not persistent must be re-executed on abort, etc.
+      ModelAPI_ExecState aState = theFeature->data()->execState();
+      if (aFactory->validate(theFeature)) {
+        executeFeature(theFeature);
+      } else {
+        theFeature->eraseResults();
+        redisplayWithResults(theFeature, ModelAPI_StateInvalidArgument); // result also must be updated
+      }
+    } else {
+      redisplayWithResults(theFeature, ModelAPI_StateNothing);
+      if (theFeature->data()->execState() == ModelAPI_StateMustBeUpdated) { // it is done (in the tree)
+        theFeature->data()->execState(ModelAPI_StateDone);
+      }
+    }
+    return;
+  }
+
   // execute feature if it must be updated
-  if (theFeature->isPreviewNeeded()) {
-    if ((myIsAutomatic || aJustUpdated) &&
-      std::dynamic_pointer_cast<Model_Document>(theFeature->document())->executeFeatures()) {
-        ModelAPI_ExecState aState = theFeature->data()->execState();
-        if (aFactory->validate(theFeature)) {
-          executeFeature(theFeature);
-        } else {
-          theFeature->eraseResults();
-          redisplayWithResults(theFeature, ModelAPI_StateInvalidArgument); // result also must be updated
-        }
+  if (theFeature->isPreviewNeeded() || myIsFinish) {
+    if (aJustUpdated) {
+      ModelAPI_ExecState aState = theFeature->data()->execState();
+      if (aFactory->validate(theFeature)) {
+        #ifdef DEB_UPDATE
+          std::cout<<"Execute Feature "<<theFeature->name()<<std::endl;
+        #endif
+        executeFeature(theFeature);
+      } else {
+        theFeature->eraseResults();
+        redisplayWithResults(theFeature, ModelAPI_StateInvalidArgument); // result also must be updated
+      }
     }
   } else { // preview is not needed => make state Done
     if (theFeature->data()->execState() == ModelAPI_StateMustBeUpdated) {
       theFeature->data()->execState(ModelAPI_StateDone);
+      if (aJustUpdated) // store that it must be updated on finish
+        myJustUpdated.insert(theFeature);
     }
   }
 }
@@ -268,7 +320,8 @@ void Model_Update::redisplayWithResults(FeaturePtr theFeature, const ModelAPI_Ex
   std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
   for (; aRIter != aResults.cend(); aRIter++) {
     std::shared_ptr<ModelAPI_Result> aRes = *aRIter;
-    aRes->data()->execState(theState);
+    if (!aRes->isDisabled()) // update state only for enabled results (Placement Result Part may make the original Part Result as invalid)
+      aRes->data()->execState(theState);
     if (theFeature->data()->updateID() > aRes->data()->updateID()) {
       aRes->data()->setUpdateID(theFeature->data()->updateID());
     }
@@ -296,11 +349,14 @@ ModelAPI_ExecState stateByReference(ObjectPtr theTarget, const ModelAPI_ExecStat
 
 void Model_Update::updateArguments(FeaturePtr theFeature) {
   // perform this method also for disabled features: to make "not done" state for
-  // featuers referenced to the active and modified features
+  // features referenced to the active and modified features
 
   static ModelAPI_ValidatorsFactory* aFactory = ModelAPI_Session::get()->validators();
 
   ModelAPI_ExecState aState = theFeature->data()->execState();
+  if (aState == ModelAPI_StateExecFailed) { // try again failed feature: issue 577
+    aState = ModelAPI_StateMustBeUpdated;
+  }
   if (aState == ModelAPI_StateInvalidArgument) // a chance to be corrected
     aState = ModelAPI_StateMustBeUpdated;
   // check the parameters state
@@ -370,7 +426,8 @@ void Model_Update::updateArguments(FeaturePtr theFeature) {
     ObjectPtr aContext = aSel->context();
     // update argument onlt if the referenced object is changed
     if (aContext.get() && !aContext->isDisabled() && 
-      aContext->data()->updateID() > theFeature->data()->updateID()) {
+      (myJustUpdated.find(aContext) != myJustUpdated.end() ||
+      aContext->data()->updateID() > theFeature->data()->updateID())) {
         if (aState == ModelAPI_StateDone)
           aState = ModelAPI_StateMustBeUpdated;
         if (!aSel->update()) { // this must be done on execution since it may be long operation
@@ -391,7 +448,8 @@ void Model_Update::updateArguments(FeaturePtr theFeature) {
         ObjectPtr aContext = aSelAttr->context();
         // update argument onlt if the referenced object is changed
         if (aContext.get() && !aContext->isDisabled() &&
-          aContext->data()->updateID() > theFeature->data()->updateID()) {
+            (myJustUpdated.find(aContext) != myJustUpdated.end() ||
+             aContext->data()->updateID() > theFeature->data()->updateID())) {
             if (aState == ModelAPI_StateDone)
               aState = ModelAPI_StateMustBeUpdated;
             if (!aSelAttr->update()) {
@@ -427,6 +485,32 @@ void Model_Update::updateArguments(FeaturePtr theFeature) {
       aState = stateByReference(*aRefObj, aState);
     }
   }
+  // composites sub-elements
+  CompositeFeaturePtr aCompos = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theFeature);
+  // composite feature must be executed after sub-features execution
+  if (aCompos) {
+    // number of subs can be changed in execution: like fillet
+    for(int a = 0; a < aCompos->numberOfSubs(); a++) {
+      FeaturePtr aSub = aCompos->subFeature(a);
+      if (aSub.get() && aState == ModelAPI_StateDone) {
+        if (myJustUpdated.find(aSub) != myJustUpdated.end() || 
+              (aSub->data()->updateID() > theFeature->data()->updateID())) {
+          aState = ModelAPI_StateMustBeUpdated;
+        }
+        // also check that all results of subs were updated: composite also depends on the results
+        const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aSub->results();
+        std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aResIter = aResults.begin();
+        for(; aResIter != aResults.end(); aResIter++) {
+          if (aResIter->get() && (*aResIter)->data()->isValid() && !(*aResIter)->isDisabled() &&
+                (myJustUpdated.find(*aResIter) != myJustUpdated.end() || 
+                  ((*aResIter)->data()->updateID() > theFeature->data()->updateID()))) {
+            aState = ModelAPI_StateMustBeUpdated;
+          }
+        }
+      }
+    }
+  }
+
 
   if (aState != ModelAPI_StateDone)
     theFeature->data()->execState(aState);
@@ -444,6 +528,7 @@ void Model_Update::executeFeature(FeaturePtr theFeature)
       aState = ModelAPI_StateExecFailed;
     } else {
       aState = ModelAPI_StateDone;
+      myWaitForFinish.insert(theFeature);
     }
   } catch(...) {
     aState = ModelAPI_StateExecFailed;