]> gerrit.simantics Code Review - simantics/3d.git/blob - org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/PipelineComponent.java
Remove listener calls when property values not updated.
[simantics/3d.git] / org.simantics.plant3d / src / org / simantics / plant3d / scenegraph / PipelineComponent.java
1 package org.simantics.plant3d.scenegraph;
2
3 import java.util.Collections;
4 import java.util.HashMap;
5 import java.util.Map;
6 import java.util.Map.Entry;
7 import java.util.Objects;
8
9 import javax.vecmath.Quat4d;
10 import javax.vecmath.Tuple3d;
11 import javax.vecmath.Vector3d;
12
13 import org.simantics.g3d.math.MathTools;
14 import org.simantics.g3d.property.annotations.CompoundGetPropertyValue;
15 import org.simantics.g3d.property.annotations.CompoundSetPropertyValue;
16 import org.simantics.g3d.property.annotations.GetPropertyValue;
17 import org.simantics.g3d.property.annotations.PropertyContributor;
18 import org.simantics.objmap.graph.annotations.CompoundRelatedGetValue;
19 import org.simantics.objmap.graph.annotations.CompoundRelatedSetValue;
20 import org.simantics.objmap.graph.annotations.RelatedGetObj;
21 import org.simantics.objmap.graph.annotations.RelatedSetObj;
22 import org.simantics.plant3d.ontology.Plant3D;
23 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint;
24 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.Direction;
25 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.PointType;
26 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.PositionType;
27 import org.simantics.plant3d.scenegraph.controlpoint.PipingRules;
28
29 /**
30  * 
31  * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
32  *
33  */
34 @PropertyContributor
35 public abstract class PipelineComponent extends GeometryNode {
36     
37     private static boolean DEBUG = false;
38
39         
40         private PipeRun pipeRun;
41         private PipeRun alternativePipeRun;
42         private PipelineComponent next;
43         private PipelineComponent previous;
44         
45         public PipeRun getPipeRun() {
46                 return pipeRun;
47         }
48         
49         /**
50          * Sets the pipe run.
51          * 
52          * With in-line,turn, and end components, the pipe run is the parent object in the scene-graph.
53          * With nozzles, the pipe run setting is explicit (nozzle has to be linked to the piperun, since the parent object is equipment).
54          * With size change components (in-line), there is also alternative pipe run, which must match the next component's pipe run.
55          * 
56          * @param pipeRun
57          */
58         public void setPipeRun(PipeRun pipeRun) {
59                 if (pipeRun == this.pipeRun)
60                         return;
61                 this.pipeRun = pipeRun;
62                 if (getControlPoint() != null) {
63                         getControlPoint().deattach();
64                         if (pipeRun != null) {
65                                 pipeRun.addChild(getControlPoint());
66                         }
67                 }
68                 updateParameters();
69         }
70         
71         @RelatedGetObj(Plant3D.URIs.HasAlternativePipeRun)
72         public PipeRun getAlternativePipeRun() {
73                 return alternativePipeRun;
74         }
75         
76         @RelatedSetObj(Plant3D.URIs.HasAlternativePipeRun)
77         public void setAlternativePipeRun(PipeRun pipeRun) {
78                 if (this.alternativePipeRun == pipeRun)
79                         return;
80                 this.alternativePipeRun = pipeRun;
81                 if (getControlPoint().isDualInline()) {
82                         PipeControlPoint sub = getControlPoint().getDualSub();
83                         if (sub.getParent() != this.alternativePipeRun) {
84                             if (this.alternativePipeRun != null) {
85                                this.alternativePipeRun.addChild(sub);
86                             } else if (sub.getPipeRun() != null) {
87                                 // FIXME : how to handle child point without proper run?
88                                 sub.getPipeRun().remChild(sub);
89                             }
90                         }
91                                 
92                 }
93                 firePropertyChanged(Plant3D.URIs.HasAlternativePipeRun);
94         }
95         
96         @Override
97         public void updateParameters() {
98                 setParameterMap(updateParameterMap());
99                 super.updateParameters();
100         }
101         
102         @Override
103     @CompoundRelatedGetValue(objRelation=Plant3D.URIs.hasParameter,objType=Plant3D.URIs.Parameter,valRelation=Plant3D.URIs.hasParameterValue)
104     public Map<String, Object> getParameterMap() {
105         return super.getParameterMap();
106     }
107         
108     @Override
109     @CompoundRelatedSetValue(Plant3D.URIs.hasParameter)
110     public void setParameterMap(Map<String, Object> parameters) {
111         super.setParameterMap(parameters);
112     }
113     
114     @CompoundGetPropertyValue(name="Parameters",tabId="Parameters",value="parameters")
115     public Map<String,Object> getParameterMapUI() {
116         // TODO : how to filter parameters that are calculated by geometry provider?
117         Map<String,Object> map = new HashMap<String, Object>(getParameterMap());
118         map.remove("radius");
119         map.remove("radius2");
120         map.remove("offset");
121         return map;
122     }
123     
124     @CompoundSetPropertyValue(value="parameters")
125     public void setParameterMapUI(Map<String, Object> parameters) { 
126         Map<String, Object> curr = getParameterMap();
127         for (Entry<String, Object> entry : curr.entrySet()) {
128             if (!parameters.containsKey(entry.getKey()))
129                 parameters.put(entry.getKey(), entry.getValue());
130         }
131         setParameterMap(parameters);
132     }
133     
134     public void setParameter(String name, Object value) {
135         Map<String, Object> parameters = new HashMap<>(getParameterMap());
136         parameters.put(name, value);
137         setParameterMap(parameters);
138     }
139         
140         public abstract void setType(String typeURI) throws Exception;
141         
142         @RelatedGetObj(Plant3D.URIs.HasNext)
143         public PipelineComponent getNext() {
144                 return next;
145         }
146         
147         @RelatedSetObj(Plant3D.URIs.HasNext)
148         public void setNext(PipelineComponent comp) {
149                 if (next == comp) {
150                     syncNext();
151                     return;
152                 }
153                 if (this.next != null)
154                     this.next._removeRef(this);
155         _setNext(comp);
156         this.syncnext = false;
157         if (DEBUG) System.out.println(this + " next " + comp);
158         syncNext();
159         firePropertyChanged(Plant3D.URIs.HasNext);
160         if (comp != null)
161             comp.sync();
162         }
163         
164         protected void _setNext(PipelineComponent comp) {
165             this.next = comp;
166         }
167         
168         
169         @RelatedGetObj(Plant3D.URIs.HasPrevious)
170         public PipelineComponent getPrevious() {
171                 return previous;
172         }
173         
174         @RelatedSetObj(Plant3D.URIs.HasPrevious)
175         public void setPrevious(PipelineComponent comp) {
176                 if (previous == comp) {
177                     syncPrevious();
178                         return;
179                 }
180                 if (this.previous != null)
181                         this.previous._removeRef(this);
182                 _setPrevious(comp);
183                 this.syncprev = false;
184                 if (DEBUG) System.out.println(this + " prev " + comp);
185                 syncPrevious();
186                 firePropertyChanged(Plant3D.URIs.HasPrevious);
187                 if (comp != null)
188                         comp.sync();
189         }
190         
191         protected void _setPrevious(PipelineComponent comp) {
192         this.previous = comp;
193     }
194         
195         private PipelineComponent branch0;
196         
197         @RelatedGetObj(Plant3D.URIs.HasBranch0)
198         public PipelineComponent getBranch0() {
199                 return branch0;
200         }
201         
202         @RelatedSetObj(Plant3D.URIs.HasBranch0)
203         public void setBranch0(PipelineComponent comp) {
204                 if (branch0 == comp) {
205                     syncBranch0();
206                         return;
207                 }
208                 if (this.branch0 != null)
209                         this.branch0._removeRef(this);
210                 this.branch0 = comp;
211                 this.syncbr0 = false;
212                 if (DEBUG) System.out.println(this + " br0 " + comp);
213                 syncBranch0();
214                 firePropertyChanged(Plant3D.URIs.HasBranch0);
215                 if (comp != null)
216                         comp.sync();
217         }
218
219         @GetPropertyValue(name="Previous",tabId="Debug",value=Plant3D.URIs.HasPrevious)
220         public String getPreviousDebug() {
221                 if (previous == null)
222                         return null;
223                 return previous.getName();
224         }
225         
226         @GetPropertyValue(name="Next",tabId="Debug",value=Plant3D.URIs.HasNext)
227         public String getNextDebug() {
228                 if (next == null)
229                         return null;
230                 return next.getName();
231         }
232         
233         @GetPropertyValue(name="Branch0",tabId="Debug",value=Plant3D.URIs.HasBranch0)
234         public String getBR0Debug() {
235                 if (branch0 == null)
236                         return null;
237                 return branch0.getName();
238         }
239
240         
241         
242         private PipeControlPoint getBranchPoint() {
243                 PipeControlPoint branchPoint;
244                 if (getControlPoint().getChildPoints().size() > 0) {
245                         branchPoint = getControlPoint().getChildPoints().get(0);
246                 } else {
247                         if (branch0.getPipeRun() == null)
248                                 return null;
249                         branchPoint = new PipeControlPoint(this,branch0.getPipeRun());
250                         branchPoint.setFixed(false);
251                         branchPoint.setType(PointType.END);
252                         branchPoint.parent = getControlPoint();
253                         getControlPoint().children.add(branchPoint);
254                         branchPoint.setWorldOrientation(getControlPoint().getWorldOrientation());
255                         branchPoint.setWorldPosition(getControlPoint().getWorldPosition());
256                 }
257                 return branchPoint;
258         }
259         
260         private boolean _connectNext(PipeControlPoint pcp, PipeControlPoint nextPCP) {
261                 if (nextPCP == null)
262                         return false;
263                 if (pcp.getNext() != nextPCP) {
264                         pcp.setNext(nextPCP);
265                 }
266 //              if (pcp.isDualInline()) {
267 //                      PipeControlPoint sub = pcp.getChildPoints().get(0);
268 //                      if (sub.getNext() != nextPCP)
269 //                              sub.setNext(nextPCP);
270 //              }
271                 return true;
272         }
273         
274         private boolean  _connectPrev(PipeControlPoint pcp, PipeControlPoint prevPCP) {
275                 if (prevPCP == null)
276                         return false;
277 //              if (prevPCP.isDualInline())
278 //                      prevPCP = prevPCP.getChildPoints().get(0);
279                 if (pcp.getPrevious() != prevPCP) {
280                         pcp.setPrevious(prevPCP);
281                 }
282 //              if (pcp.isDualInline()) {
283 //                      PipeControlPoint sub = pcp.getChildPoints().get(0);
284 //                      if (sub.getPrevious() != prevPCP)
285 //                              sub.setPrevious(prevPCP);
286 //              }
287                 return true;
288         }
289         
290         // When link to a component is removed, also link to the other direction must be removed at the same time, or
291         // Control point structure is left into illegal state.
292         private void _removeRef(PipelineComponent comp) {
293                 if (next == comp) {
294                         _setNext(null);
295                         syncnext = false;
296                         if (DEBUG) System.out.println(this + " remove next " + comp);
297                         firePropertyChanged(Plant3D.URIs.HasNext);
298                         syncNext();
299                 } else if (previous == comp) {
300                         _setPrevious(null);
301                         syncprev = false;
302                         if (DEBUG) System.out.println(this + " remove prev " + comp);
303                         firePropertyChanged(Plant3D.URIs.HasPrevious);
304                         syncPrevious();
305                 } else if (branch0 == comp) {
306                         branch0 = null;
307                         syncbr0 = false;
308                         if (DEBUG) System.out.println(this + " remove br0 " + comp);
309                         firePropertyChanged(Plant3D.URIs.HasBranch0);
310                         syncBranch0();
311                 }
312         }
313         
314         boolean syncnext = false;
315         protected void syncNext() {
316                 if (syncnext)
317                         return;
318                 syncnext = _syncNext();
319         }
320         
321         
322         private boolean _syncNext() {
323                 PipeControlPoint pcp = getControlPoint();
324                 if (pcp != null) {
325                         
326                         if (next != null ) {
327                                 if (next.getControlPoint() != null) {
328                                         
329                                         // TODO, relying that the other direction is connected.
330                                         boolean nxt = next.getPrevious() == this;
331                                         boolean br0 = next.getBranch0() == this;
332                                         if (nxt){
333                                                 return _connectNext(pcp, next.getControlPoint());       
334                                         } else if (br0) {
335                                                 return _connectNext(pcp, next.getBranchPoint());
336                                         } else {
337                                                 return false;
338                                         }
339                                 } else {
340                                         return false;
341                                 }
342                                 
343                         } else if (pcp.getNext() != null) {
344                                 pcp.setNext(null);
345                                 return true;
346                         }
347                 } else {
348                         return false;
349                 }
350                 return true;
351         }
352         
353         boolean syncprev = false;
354         protected void syncPrevious() {
355                 if (syncprev)
356                         return;
357                 syncprev = _syncPrevious();
358         }
359         
360         private boolean _syncPrevious() {
361                 PipeControlPoint pcp = getControlPoint();
362                 if (pcp != null) {
363                         if (previous != null ) {
364                                 if (previous.getControlPoint() != null) {
365                                         
366                                         // TODO, relying that the other direction is connected.
367                                         boolean prev = previous.getNext() == this;
368                                         boolean br0 = previous.getBranch0() == this;
369                                         if (prev){
370                                                 return _connectPrev(pcp, previous.getControlPoint());   
371                                         } else if (br0) {
372                                                 return _connectPrev(pcp, previous.getBranchPoint());
373                                         } else {
374                                                 return false;
375                                         }
376                                 } else {
377                                         return false;
378                                 }
379                                 
380                         } else if (pcp.getPrevious() != null) {
381                                 pcp.setPrevious(null);
382                                 return true;
383                         }
384                 } else {
385                         return false;
386                 }
387                 return true;
388         }
389         
390         boolean syncbr0 = false;
391         protected void syncBranch0() {
392                 if (syncbr0)
393                         return;
394                 syncbr0 = _syncBranch0();
395         }
396         
397         private boolean _syncBranch0() {
398                 if (getControlPoint() != null) {
399                         if (getControlPoint().isDualInline()) {
400                                 branch0 = null;
401                                 return false;
402                         }
403                         if (branch0 != null) {
404                                 if (branch0.getControlPoint() != null) {
405                                         PipeControlPoint branchPoint = getBranchPoint();
406                                         if (branchPoint == null)
407                                                 return false;
408                                         PipeControlPoint pcp = branch0.getControlPoint();
409                                         // TODO, relying that the other direction is connected.
410                                         boolean next = branch0.getPrevious() == this; // this --> branch0
411                                         boolean prev = branch0.getNext() == this;
412                                         if (next) {
413                                                 _connectNext(branchPoint, pcp);
414                                         } else if (prev){
415                                                 _connectPrev(branchPoint, pcp); 
416                                         } else {
417                                                 return false;
418                                         }
419                                         
420                                 } else {
421                                         return false;
422                                 }
423                                 
424                         } else if (getControlPoint().getChildPoints().size() > 0) { // TODO : this may cause problems? (Removes branch point, before branch has been set?)
425                                 //getControlPoint().getSubPoint().get(0).remove();
426                                 //getControlPoint().children.clear();
427                                 return true;
428                         }
429                 } else {
430                         return false;
431                 }
432                 return true;
433         }
434         
435         public void sync() {
436                 syncPrevious();
437                 syncNext();
438                 syncBranch0();
439         }
440         
441         public void sync2() {
442 //              if (getControlPoint().isDualInline()) {
443 //                      PipeControlPoint sub = getControlPoint().getSubPoint().get(0);
444 //                      next.getControlPoint().getPipeRun().addChild(sub);
445 //              }
446                 getControlPoint()._setWorldOrientation(getWorldOrientation());
447                 getControlPoint()._setWorldPosition(getWorldPosition());
448         }
449         
450         public Map<String,Object> updateParameterMap() {
451                 return Collections.emptyMap();
452         }
453         
454         public abstract String getType();
455         public abstract PipeControlPoint getControlPoint();
456         
457         @Override
458         public void remove() {
459             if (DEBUG) System.out.println(this + " remove");
460                 PipeControlPoint pcp = getControlPoint();
461                 // Second check is needed, when remove process is initiated from control point.
462                 if (pcp != null && pcp.getPipelineComponent() != null) {
463                         if (pcp.isSizeChange()) {
464                                 mergeWithAlternative();
465                         }
466                         
467                         pcp.remove();
468                 }
469                 
470                 setPipeRun(null);
471                 super.remove();
472         }
473
474         private void mergeWithAlternative() {
475                 PipeRun run = getPipeRun();
476                 PipeRun alternative = getAlternativePipeRun();
477                 if (alternative != null) {
478                         // Move components from alternative pipe run to main run
479                         PipelineComponent p = getNext();
480                         while (p != null && p.getPipeRun() == alternative) {
481                                 if (p.getParent() == alternative) {
482                                         p.deattach(); // For components
483                                         run.addChild(p);
484                                 }
485                                 else {
486                                         p.setPipeRun(run); // For nozzles
487                                 }
488
489                                 p.updateParameters();
490                                 PipingRules.requestUpdate(p.getControlPoint());
491                                 
492                                 p = p.getNext();
493                         }
494                         
495                         setAlternativePipeRun(run);
496                         
497                         if (alternative.getChild().isEmpty())
498                                 alternative.remove();
499                 }
500         }
501         
502         public void removeAndSplit() {
503             PipeControlPoint pcp = getControlPoint();
504         // Second check is needed, when remove process is initiated from control point.
505         if (pcp != null && pcp.getPipelineComponent() != null) {
506             pcp.removeAndSplit();
507         }
508         setPipeRun(null);
509         super.remove();
510         }
511
512         @Override
513         protected double[] getColor() {
514                 if (getControlPoint() == null || !getControlPoint().isFixed())
515                         return new double[]{0.7,0.7,0.7};
516                 else
517                         return new double[]{1.0,0.0,0.0};
518         }
519         
520         @Override
521         protected double[] getSelectedColor() {
522                 return new double[]{0.5,0,0.5};
523         }
524         
525         @Override
526         public void setOrientation(Quat4d orientation) {
527                 if (MathTools.equals(orientation, getOrientation()))
528                         return;
529                 super.setOrientation(orientation);
530                 if (getControlPoint() != null) {
531                         getControlPoint()._setWorldOrientation(getWorldOrientation());
532                         PipingRules.requestUpdate(getControlPoint());
533                 }
534         }
535         
536         @Override
537         public void setPosition(Vector3d position) {
538                 if (MathTools.equals(position, getPosition()))
539                         return;
540                 super.setPosition(position);
541                 if (getControlPoint() != null) {
542                         getControlPoint()._setWorldPosition(getWorldPosition());
543                         PipingRules.requestUpdate(getControlPoint());
544                 }
545         }
546         
547         
548         public void _setWorldPosition(Vector3d position) {
549                 Vector3d localPos = getLocalPosition(position);
550                 super.setPosition(localPos);
551         }
552         
553         public void _setWorldOrientation(Quat4d orientation) {
554                 Quat4d localOr = getLocalOrientation(orientation);
555                 super.setOrientation(localOr);
556         }
557         
558         @GetPropertyValue(name="Flow Length", value="flowlength", tabId = "Default")
559         public Double getFlowLength() {
560                 PipeControlPoint pcp = getControlPoint(); 
561                 if (pcp == null)
562                         return null;
563                 switch (pcp.getType()) {
564                         case INLINE:
565                                 return pcp.getLength();
566                         case END:
567                                 return null;
568                         case TURN: {
569                                 double r = ((TurnComponent)this).getTurnRadius();
570                                 double a = pcp.getTurnAngle();
571                                 return a*r;
572                         }
573                         default:
574                                 return null;
575                 }
576         }
577         
578         /**
579          * Returns diameter of the pipe
580          * @return
581          */
582         public Double getDiameter() {
583             return getPipeRun().getPipeDiameter();
584         }
585         
586         /**
587          * Returns secondary diameter of the pipe for size change components
588          * @return
589          */
590         public Double getDiameter2() {
591             if (getAlternativePipeRun() == null)
592                 return null;
593             return getAlternativePipeRun().getPipeDiameter();
594         }
595         
596         public void getEnds(Tuple3d p1, Tuple3d p2) {
597                 getControlPoint().getControlPointEnds(p1, p2);
598         }
599         
600         public void getEndDirections(Tuple3d v1, Tuple3d v2) {
601                 getControlPoint().getEndDirections(v1, v2);
602         }
603         
604         public void getCentroid(Tuple3d p) {
605                 PipeControlPoint pcp = getControlPoint(); 
606                 if (pcp == null)
607                         throw new IllegalStateException("No centroid defined");
608                 
609                 switch (pcp.getType()) {
610                 case INLINE:
611                 case END:
612                         // Just return the world location
613                         if (!pcp.isSizeChange()) {
614                                 p.set(pcp.getWorldPosition());
615                                 return;
616                         }
617                         
618                         // Calculate center of mass for the frustum
619                         double r1 = getPipeRun().getPipeDiameter();
620                         double r2 = getAlternativePipeRun().getPipeDiameter();
621                         
622                         Vector3d p1 = new Vector3d(), p2 = new Vector3d();
623                         pcp.getInlineControlPointEnds(p1, p2);
624                         
625                         // Squared sum of radii
626                         double r12 = r1 + r2;
627                         r12 *= r12;
628                         
629                         // The larger of the radii form the base of a frustum
630                         double rmax = Math.max(r1, r2);
631                         
632                         // Relative distance from the base of the frustum
633                         double h = (r12 + 2*rmax*rmax) / (4 * (r12 - r1*r2));
634                         
635                         // Relative distance from p1 to p2
636                         if (r1 < r2)
637                                 h = 1 - h;
638                         
639                         p2.sub(p1);
640                         p1.scaleAdd(h, p2);
641                         
642                         p.set(p1);
643                         return;
644                 case TURN: {
645                         Vector3d loc = pcp.getRealPosition(PositionType.PREVIOUS);
646                         
647                         double r = ((TurnComponent)this).getTurnRadius();
648                         double a = pcp.getTurnAngle();
649                         double pipeRadius = pcp.getPipeRun().getPipeDiameter() / 2;
650                         
651                         // Unit vector in inlet flow direction
652                         Vector3d inletDir = pcp.getPathLegDirection(Direction.PREVIOUS);
653                         inletDir.scale(-1.0);
654                         inletDir.normalize();
655                         
656                         // Normal to both inletDir and turn axis in world coordinates
657                         Vector3d outletDir = pcp.getPathLegDirection(Direction.NEXT);
658                         Vector3d normal = new Vector3d(inletDir);
659                         normal.scaleAdd(-inletDir.dot(outletDir), outletDir);
660                         normal.normalize();
661                         
662                         // Location of turn axis
663                         Vector3d center = new Vector3d(normal);
664                         center.scaleAdd(r, loc);
665                         
666                         // Add vector components from axis to centroid
667                         double c = r + pipeRadius * pipeRadius / (4 * r);
668                         double c1 = c * Math.sin(a) / a;
669                         double c2 = c * (1 - Math.cos(a)) / a;
670                         normal.scale(-c1);
671                         inletDir.scale(c2);
672                         center.add(normal);
673                         center.add(inletDir);
674                 
675                         // Return value
676                         p.set(center);
677                         return;
678                 }
679                 default:
680                         throw new IllegalStateException("No centroid defined");
681                 }
682         }
683         
684         public double getVolume() {
685                 PipeControlPoint pcp = getControlPoint(); 
686                 if (pcp == null)
687                         throw new IllegalStateException("No centroid defined");
688                 
689                 double pipeRadius = getPipeRun().getPipeDiameter() / 2;
690                 
691                 switch (pcp.getType()) {
692                 case INLINE:
693                 case END:
694                         if (!pcp.isSizeChange()) {
695                                 // Just return the cylinder volume
696                                 return pcp.getLength() * Math.PI * pipeRadius * pipeRadius;
697                         }
698                         
699                         // Calculate center of mass for the frustum
700                         double r1 = pipeRadius;
701                         double r2 = getAlternativePipeRun().getPipeDiameter() / 2;
702                         return pcp.getLength() * Math.PI * (r1*r1 + r1*r2 + r2*r2) / 4;
703                 case TURN: {
704                         double r = ((TurnComponent)this).getTurnRadius();
705                         double a = pcp.getTurnAngle();
706                         return r * a * Math.PI * pipeRadius * pipeRadius;
707                 }
708                 default:
709                         throw new IllegalStateException("No centroid defined");
710                 }
711         }
712         
713         
714         private String error;
715         
716         /**
717          * Returns possible pipe modelling error, or null;
718          * @return
719          */
720         @GetPropertyValue(name="Error", value="error", tabId = "Default")
721         public String getError() {
722             return error;
723         }
724         
725         /**
726          * Sets pipe modelling error. 
727          * 
728          * Error is usually set by PipingRules.
729          * @param error
730          */
731         public void setError(String error) {
732             if (Objects.equals(this.error,  error))
733                 return;
734             this.error = error;
735             firePropertyChanged("error");
736         }
737 }