This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Using WaterGEMS tools in WaterObjects.NET

Is it possible to use/call the various tools that are present in WaterGEMS through WaterObjects.NET? I'm mainly interested in the following tools:

- Reverse pipe

- Batch Morph

- Batch Pipe Split

I've already tried to reverse pipes in WaterObjects by interchanging the start and stop nodes of a pipe, and reversing the GeometryPoint[] variable of the pipe (vertices). This seems to work, however when I open the model, the check-valve icon of the pipe is still facing the wrong direction, this can be manualy fixed by updating the drawing though. Is it possible to call the update of the drawing through WaterObjects.NET? I could also separately program each step of the Batch Morph tool, but using the tool directly in WaterObjects.NET would be much easier...

The reverse pipe tool is mainly used to make sure the check-valve in the pipe is working in the correct direction. The batch morph tool will mainly be used to convert taps to junctions after which the batch pipe split tool is used to split the pipes. 

Parents
  • Is it possible to use/call the various tools that are present in WaterGEMS through WaterObjects.NET? I'm mainly interested in the following tools:

    - Reverse pipe

    - Batch Morph

    - Batch Pipe Split

    I checked with one of our developers who indicated that this could get a bit complicated. Could you tell us a bit more about why you need to perform such functions using a Waterobjects.NET tool? What version of WaterObjects/WaterGEMS are you using?

    This seems to work, however when I open the model, the check-valve icon of the pipe is still facing the wrong direction, this can be manualy fixed by updating the drawing though. Is it possible to call the update of the drawing through WaterObjects.NET?

    The appearance of the drawing pane (including the pipes and nodes and "decorations" like the check valve symbol) is stored in the model's .DWH file. Deleting this file would force a re-sync when re-opening the model. Here is some additional feedback from one of our developers:

    If they are opening just the SQLite database using WO.Net, then they may need to check this:

    dataSource.SetConnectionProperty(ConnectionProperty.ShouldUpdateCounters, true);

    This is would be done when they use ConnectionProperty.Filename.  This flag will make sure that internal counters are incremented when geometry is changed (like he does with reverse pipe).  If the counter is correctly incremented, this should also trigger a drawing sync on opening in the UI.


    Regards,

    Jesse Dringoli
    Technical Support Manager, OpenFlows
    Bentley Communities Site Administrator
    Bentley Systems, Inc.

  • Dear Jesse,

    All these tools will be used in a more elaborate workflow, I'd be happy to explain it in more detail in a non-public conference call. 

    I'm opening the model using the following code:

    ProjectManager.OpenProject(NewProjectProperties(ModelDatabasePath));

    I believe it is part of Haestad.Framework.Application

  • Hi Joeri, one of our developers will reply here shortly. Thanks for your patience.


    Regards,

    Jesse Dringoli
    Technical Support Manager, OpenFlows
    Bentley Communities Site Administrator
    Bentley Systems, Inc.

  • Hi Joeri,

    I apologize for the delayed response.

    Unfortunately, these tools can be complicated to use with WaterObjects.NET as they are tightly integrated into the UI (so it makes it difficult not to use them without the UI).  I'll provide some information here to hopefully get you going with them.  If you need clarification on anything, please ask.

    Reverse Pipe:

    For reverse pipe you will need:

    ParentFormUIModel (for LayoutController), IDomainProject (for DomainDataSet), DomainManager (can get from LayoutController)

    // DomainDataSet is assembed to be from an open IDomainProject.  
    // DomainManager is also assumed to be valid
    // This sample code will reverse the nodes of all pipes in a WaterGEMS model.
    // You also need a valid IProject as well. Since you indicate you are opening the model with ProjectManager, you should have it.
    
    IDomainElementManager pipeManager = DomainDataSet.DomainElementManager((int)DomainElementType.IdahoPipeElementManager);
    int linkElementType = (int)DomainElementType.IdahoPipeElementManager;
    
    // Using a "using" block here is important so that the IHmIDDelayedCollection that is created is correctly disposed.   Event
    // though ElementIDs() returns an HmIDCollection it is really, internally, an IHmIDDelayedCollection.
    using (HmIDCollection pipeIDs = pipeManager.ElementIDs())
    {
        IField upstreamNodeIdField = pipeManager.DomainElementField(StandardFieldName.HmiTopologyStartNodeID, StandardAlternativeName.HmiTopology);
        IField downstreamNodeIdField = pipeManager.DomainElementField(StandardFieldName.HmiTopologyStopNodeID, StandardAlternativeName.HmiTopology);
    
        IHmIDToObjectDictionary upstreamNodeIDs = (IHmIDToObjectDictionary)upstreamNodeIdField.GetValues();
        IHmIDToObjectDictionary downstreamNodeIDs = (IHmIDToObjectDictionary)downstreamNodeIdField.GetValues();
        
        // At this point, a simple loop can be used to batch reverse the pipes.
        for (int i = 0; i < pipeIDs.Count; ++i)
        {
            int pipeID = pipeIDs[i];
            
            object objUpstreamNodeID = upstreamNodeIDs[pipeID];
            object objDownstreamNodeID = downstreamNodeIDs[pipeID];
            
            if (objUpstreamNodeID is int upstreamNodeID &&
                objDownstreamNodeID is int downstreamNodeID)
            {
                // This check will make sure both the start and stop 
                // node IDs are set.  If either are undefined (dictionary will have value of null for pipe id)
                // it will skip the pipe from being reversed.
                
                int upstreamNodeType = DomainDataSet.DomainElementTypeID(upstreamNodeID);
                int downstreamNodeType = DomainDataSet.DomainElementTypeID(downstreamNodeID);
                
                ElementIdentifier startNodeIdentifier = new ElementIdentifier(upstreamNodeType, upstreamNodeID);
                ElementIdentifier stopNodeIdentifier = new ElementIdentifier(downstreamNodeType, downstreamNodeID);
                ElementIdentifier linkIdentifier = new ElementIdentifier(linkElementType, pipeID);
                
                // This call does the reversal.  It take care of the geometry as well.
                // Also keep in mind that this call will create an undoable action.  It uses the UndoManager from the provided Project
                DomainManager.ReverseStartStopNodes(linkIdentifier, startNodeIdentifier, stopNodeIdentifier, (ILabeledElementDomainProject)project);
            }
        }
    }
    
    // Because you mentioned that the drawing didn't seem to update, you can use this call to do it.  I am using a NullProgressIndicator here
    // but you can use a ProgressIndicatorForm instead to show visual status.  Up to you.
    // This call requires the LayoutController.  This can be retrieed from the ParentFormUIModel which I assume you are using as well.
    ParentFormUIModel.LayoutController.SynchronizeWithDatabase(IGraphicalProject)project, new NullProgressIndicator());

    Batch morph is nearly as easy as reverse.  Again, it will use the DomainManager to do the bulk of the work.  You will need the same required items listed for reverse.  FeatureManager is also required but that can be retrieved from casting the ApplicationModel to IMappingApplicationModel.

    // For this code, you need to provide a list of node ids to morph into a target element type.
    // Any ids that are already of the target element type will be ignored.
    // You should EXCLUDE ids of the types ISO valves, spot elevations, SCADA elements.  These
    // types cannot be morphed.  The UI code automatically excludes these but this code will
    // not check for them.  I am assuming you will NOT include these types in the list of IDs
    // to morph.
    
    // This method will also require the IFeatureManager.  You can get this by casting the
    // ApplicationModel to IMappingApplicationModel and using the property on that interface
    
    public void PerformBatchMorph(int targetElementType, HmIDCollection ids)
    {
        if (DomainManager is IBatchDomainManager batchDomainManager)
            batchDomainManager.BeginBatch();
        
        try
        {
            FeatureManager.BeginUpdateFeatures(Project);
            
            // Create a composite undoable action which will be provided to the morph method, HOWEVER,
            // for this code, it will NOT be added to the project's undo manager (unless you modify the code to do that).
            CompositeUndoableAction action = m_project.UndoManager.NewCompositeUndoableAction(TextManager.Current["undoActionBatchMorph"]);
            
            for (int i = 0; i < ids.Count; ++i)
            {
                int nodeID = ids[i];
                int typeID = DomainDataSet.DomainElementTypeID(nodeID);
                
                if (typeID != targetElementType)
                {
                    // If the node is NOT the target element, then morph
                    IGeometryPointField geometryField = (IGeometryPointField)DomainDataSet.FieldManager.DomainElementField(
                        StandardFieldName.HmiGeometry, (int)AlternativeType.HmiDataSetGeometryAlternative, typeID);
    
                    GeometryPoint point = geometryField.GetPoint(nodeID);
                    if (((IGraphicalProject)m_project).Drawing != null)
                    {
                        point = ((IGraphicalProject)m_project).Drawing.ConvertPhysicalToWorldCoordinate(point);
                    }
                    DomainManager.MorphNode(targetElementType, new ElementIdentifier(typeID, nodeID), point,
                        null, DomainManager.CurrentProject, action, true);
                }
            }
        }
        finally
        {
            bool needsRefresh = !action.IsEmpty;
            
            if (needsRefresh)
            {
                // For now, NOT adding the composite action to the undo manager.
                // You can optionally uncomment this code.
                // Project.UndoManager.AddAction(action);
                // ((ProjectBase)Project).MakeDirty();
            }
        
            if (DomainManager is IBatchDomainManager batchDomainManager)
                batchDomainManager.EndBatch();
                
            FeatureManager.EndUpdateFeatures(Project, needsRefresh);
        }
    }

    Batch Pipe Split

    This is the most complicated of these three tools.  I need to review the code in more detail to extract out what you need as it is much more tightly integrated with the UI than these two tools.  For now, see if you can get batch reverse and morph working in your code and hopefully by the time you are done with that, I will have something for batch pipe split.

    Again, if you have ANY questions, just ask.

    Kris Culin
    Senior Software Engineer
    Civil Engineering
    Bentley Systems, Inc.

    Answer Verified By: Joeri Legierse 

  • Dear Kris,

    Thanks a lot! I will give this a try, having quickly read the code you propose, I can understand what is being done in sequence. It contains the puzzle pieces I was missing!

Reply Children
No Data