Salome HOME
print output of BLSURF
[plugins/blsurfplugin.git] / src / BLSURFPlugin / BLSURFPlugin_BLSURF.cxx
1 //  BLSURFPlugin : C++ implementation
2 //
3 //  Copyright (C) 2006  OPEN CASCADE, CEA/DEN, EDF R&D
4 // 
5 //  This library is free software; you can redistribute it and/or 
6 //  modify it under the terms of the GNU Lesser General Public 
7 //  License as published by the Free Software Foundation; either 
8 //  version 2.1 of the License. 
9 // 
10 //  This library is distributed in the hope that it will be useful, 
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of 
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
13 //  Lesser General Public License for more details. 
14 // 
15 //  You should have received a copy of the GNU Lesser General Public 
16 //  License along with this library; if not, write to the Free Software 
17 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
18 // 
19 //  See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org 
20 //
21 //
22 // File    : BLSURFPlugin_BLSURF.cxx
23 // Authors : Francis KLOSS (OCC) & Patrick LAUG (INRIA) & Lioka RAZAFINDRAZAKA (CEA)
24 //           & Aurelien ALLEAUME (DISTENE)
25 // Date    : 20/03/2006
26 // Project : SALOME
27 //=============================================================================
28 using namespace std;
29
30 #include "BLSURFPlugin_BLSURF.hxx"
31 #include "BLSURFPlugin_Hypothesis.hxx"
32
33 #include <SMESH_Gen.hxx>
34 #include <SMESH_Mesh.hxx>
35 #include <SMESH_ControlsDef.hxx>
36
37 #include <SMESHDS_Mesh.hxx>
38 #include <SMDS_MeshElement.hxx>
39 #include <SMDS_MeshNode.hxx>
40
41 #include <utilities.h>
42
43 #include <list>
44 #include <vector>
45
46 #include <BRep_Tool.hxx>
47 #include <TopExp.hxx>
48 #include <TopExp_Explorer.hxx>
49 #include <TopoDS.hxx>
50 #include <NCollection_Map.hxx>
51 #include <Standard_ErrorHandler.hxx>
52
53 extern "C"{
54 #include <distene/api.h>
55 }
56
57 #include <Geom_Surface.hxx>
58 #include <Handle_Geom_Surface.hxx>
59 #include <Geom2d_Curve.hxx>
60 #include <Handle_Geom2d_Curve.hxx>
61 #include <Geom_Curve.hxx>
62 #include <Handle_Geom_Curve.hxx>
63 #include <TopoDS_Vertex.hxx>
64 #include <TopoDS_Edge.hxx>
65 #include <TopoDS_Wire.hxx>
66 #include <TopoDS_Face.hxx>
67 #include <TopoDS_Shape.hxx>
68 #include <gp_Pnt2d.hxx>
69 #include <TopTools_IndexedMapOfShape.hxx>
70 #include <BRepTools.hxx>
71
72 //=============================================================================
73 /*!
74  *  
75  */
76 //=============================================================================
77
78 BLSURFPlugin_BLSURF::BLSURFPlugin_BLSURF(int hypId, int studyId,
79                                                SMESH_Gen* gen)
80   : SMESH_2D_Algo(hypId, studyId, gen)
81 {
82   MESSAGE("BLSURFPlugin_BLSURF::BLSURFPlugin_BLSURF");
83
84   _name = "BLSURF";
85   _shapeType = (1 << TopAbs_FACE); // 1 bit /shape type
86   _compatibleHypothesis.push_back("BLSURF_Parameters");
87   _requireDescretBoundary = false;
88   _onlyUnaryInput = false;
89   _hypothesis = NULL;
90 }
91
92 //=============================================================================
93 /*!
94  *  
95  */
96 //=============================================================================
97
98 BLSURFPlugin_BLSURF::~BLSURFPlugin_BLSURF()
99 {
100   MESSAGE("BLSURFPlugin_BLSURF::~BLSURFPlugin_BLSURF");
101 }
102
103 //=============================================================================
104 /*!
105  *  
106  */
107 //=============================================================================
108
109 bool BLSURFPlugin_BLSURF::CheckHypothesis
110                          (SMESH_Mesh&                          aMesh,
111                           const TopoDS_Shape&                  aShape,
112                           SMESH_Hypothesis::Hypothesis_Status& aStatus)
113 {
114   _hypothesis = NULL;
115
116   list<const SMESHDS_Hypothesis*>::const_iterator itl;
117   const SMESHDS_Hypothesis* theHyp;
118
119   const list<const SMESHDS_Hypothesis*>& hyps = GetUsedHypothesis(aMesh, aShape);
120   int nbHyp = hyps.size();
121   if (!nbHyp)
122   {
123     aStatus = SMESH_Hypothesis::HYP_OK;
124     return true;  // can work with no hypothesis
125   }
126
127   itl = hyps.begin();
128   theHyp = (*itl); // use only the first hypothesis
129
130   string hypName = theHyp->GetName();
131
132   if (hypName == "BLSURF_Parameters")
133   {
134     _hypothesis = static_cast<const BLSURFPlugin_Hypothesis*> (theHyp);
135     ASSERT(_hypothesis);
136     if ( _hypothesis->GetPhysicalMesh() == BLSURFPlugin_Hypothesis::DefaultSize &&
137          _hypothesis->GetGeometricMesh() == BLSURFPlugin_Hypothesis::DefaultGeom )
138       //  hphy_flag = 0 and hgeo_flag = 0 is not allowed (spec)
139       aStatus = SMESH_Hypothesis::HYP_BAD_PARAMETER;
140     else
141       aStatus = SMESH_Hypothesis::HYP_OK;
142   }
143   else
144     aStatus = SMESH_Hypothesis::HYP_INCOMPATIBLE;
145
146   return aStatus == SMESH_Hypothesis::HYP_OK;
147 }
148
149 //=============================================================================
150 /*!
151  * Pass parameters to BLSURF
152  */
153 //=============================================================================
154
155 inline std::string to_string(double d)
156 {
157    std::ostringstream o;
158    o << d;
159    return o.str();
160 }
161
162 inline std::string to_string(int i)
163 {
164    std::ostringstream o;
165    o << i;
166    return o.str();
167 }
168
169 void BLSURFPlugin_BLSURF::SetParameters(const BLSURFPlugin_Hypothesis* hyp, blsurf_session_t *bls)
170 {
171   int    _topology      = BLSURFPlugin_Hypothesis::GetDefaultTopology();
172   int    _physicalMesh  = BLSURFPlugin_Hypothesis::GetDefaultPhysicalMesh();
173   double _phySize       = BLSURFPlugin_Hypothesis::GetDefaultPhySize();
174   int    _geometricMesh = BLSURFPlugin_Hypothesis::GetDefaultGeometricMesh();
175   double _angleMeshS    = BLSURFPlugin_Hypothesis::GetDefaultAngleMeshS();
176   double _angleMeshC    = BLSURFPlugin_Hypothesis::GetDefaultAngleMeshC();
177   double _gradation     = BLSURFPlugin_Hypothesis::GetDefaultGradation();
178   bool   _quadAllowed   = BLSURFPlugin_Hypothesis::GetDefaultQuadAllowed();
179   bool   _decimesh      = BLSURFPlugin_Hypothesis::GetDefaultDecimesh();
180   int    _verb          = BLSURFPlugin_Hypothesis::GetDefaultVerbosity();
181
182   if (hyp) {
183     MESSAGE("BLSURFPlugin_BLSURF::SetParameters");
184     _topology      = (int) hyp->GetTopology();
185     _physicalMesh  = (int) hyp->GetPhysicalMesh();
186     _phySize       = hyp->GetPhySize();
187     _geometricMesh = (int) hyp->GetGeometricMesh();
188     _angleMeshS    = hyp->GetAngleMeshS();
189     _angleMeshC    = hyp->GetAngleMeshC();
190     _gradation     = hyp->GetGradation();
191     _quadAllowed   = hyp->GetQuadAllowed();
192     _decimesh      = hyp->GetDecimesh();
193     _verb          = hyp->GetVerbosity();
194
195     if ( hyp->GetPhyMin() != ::BLSURFPlugin_Hypothesis::undefinedDouble() )
196       blsurf_set_param(bls, "hphymin", to_string(hyp->GetPhyMin()).c_str());
197     if ( hyp->GetPhyMax() != ::BLSURFPlugin_Hypothesis::undefinedDouble() )
198       blsurf_set_param(bls, "hphymax", to_string(hyp->GetPhyMax()).c_str());
199     if ( hyp->GetGeoMin() != ::BLSURFPlugin_Hypothesis::undefinedDouble() )
200       blsurf_set_param(bls, "hgeomin", to_string(hyp->GetGeoMin()).c_str());
201     if ( hyp->GetGeoMax() != ::BLSURFPlugin_Hypothesis::undefinedDouble() )
202       blsurf_set_param(bls, "hgeomax", to_string(hyp->GetGeoMax()).c_str());
203
204     const BLSURFPlugin_Hypothesis::TOptionValues & opts = hyp->GetOptionValues();
205     BLSURFPlugin_Hypothesis::TOptionValues::const_iterator opIt;
206     for ( opIt = opts.begin(); opIt != opts.end(); ++opIt )
207       if ( !opIt->second.empty() )
208         blsurf_set_param(bls, opIt->first.c_str(), opIt->second.c_str());
209
210   } else {
211     MESSAGE("BLSURFPlugin_BLSURF::SetParameters using defaults");
212   }
213   
214   blsurf_set_param(bls, "topo_points",       _topology > 0 ? "1" : "0");
215   blsurf_set_param(bls, "topo_curves",       _topology > 0 ? "1" : "0");
216   blsurf_set_param(bls, "topo_project",      _topology > 0 ? "1" : "0");
217   blsurf_set_param(bls, "clean_boundary",    _topology > 1 ? "1" : "0");
218   blsurf_set_param(bls, "close_boundary",    _topology > 1 ? "1" : "0");
219   blsurf_set_param(bls, "hphy_flag",         to_string(_physicalMesh).c_str());
220   blsurf_set_param(bls, "hphydef",           to_string(_phySize).c_str());
221   blsurf_set_param(bls, "hgeo_flag",         to_string(_geometricMesh).c_str());
222   blsurf_set_param(bls, "angle_meshs",       to_string(_angleMeshS).c_str());
223   blsurf_set_param(bls, "angle_meshc",       to_string(_angleMeshC).c_str());
224   blsurf_set_param(bls, "gradation",         to_string(_gradation).c_str());
225   blsurf_set_param(bls, "patch_independent", _decimesh ? "1" : "0");
226   blsurf_set_param(bls, "element",           _quadAllowed ? "q1.0" : "p1");
227   blsurf_set_param(bls, "verb",              to_string(_verb).c_str());
228 }
229
230 status_t curv_fun(real t, real *uv, real *dt, real *dtt, void *user_data);
231 status_t surf_fun(real *uv, real *xyz, real*du, real *dv,
232                   real *duu, real *duv, real *dvv, void *user_data);
233 status_t message_callback(message_t *msg, void *user_data);
234
235 //=============================================================================
236 /*!
237  *
238  */
239 //=============================================================================
240
241 bool BLSURFPlugin_BLSURF::Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape) {
242
243   MESSAGE("BLSURFPlugin_BLSURF::Compute");
244
245   if (aShape.ShapeType() == TopAbs_COMPOUND) {
246     cout << "  the shape is a COMPOUND" << endl;
247   }
248   else {
249     cout << "  the shape is UNKNOWN" << endl;
250   };
251
252   context_t *ctx =  context_new();
253   context_set_message_callback(ctx, message_callback, &_comment);
254
255   cad_t *c = cad_new(ctx);
256  
257   TopTools_IndexedMapOfShape fmap;
258   TopTools_IndexedMapOfShape emap;
259   TopTools_IndexedMapOfShape pmap;
260   vector<Handle(Geom2d_Curve)> curves;
261   vector<Handle(Geom_Surface)> surfaces;
262
263   fmap.Clear();
264   emap.Clear();
265   pmap.Clear();
266   surfaces.resize(0);
267   curves.resize(0);
268
269   int iface = 0;
270   for (TopExp_Explorer face_iter(aShape,TopAbs_FACE);face_iter.More();face_iter.Next()) {
271     TopoDS_Face f=TopoDS::Face(face_iter.Current());
272     if (fmap.FindIndex(f) > 0)
273       continue;
274     
275     fmap.Add(f);
276     iface++;
277     surfaces.push_back(BRep_Tool::Surface(f));
278     cad_face_t *fce = cad_face_new(c, iface, surf_fun, surfaces.back());  
279     cad_face_set_tag(fce, iface);
280     if(f.Orientation() != TopAbs_FORWARD){
281       cad_face_set_orientation(fce, CAD_ORIENTATION_REVERSED);
282     } else {
283       cad_face_set_orientation(fce, CAD_ORIENTATION_FORWARD);
284     }
285     
286     for (TopExp_Explorer edge_iter(f,TopAbs_EDGE);edge_iter.More();edge_iter.Next()) {
287       TopoDS_Edge e = TopoDS::Edge(edge_iter.Current());
288       int ic = emap.FindIndex(e);
289       if (ic <= 0)
290         ic = emap.Add(e);
291       
292       double tmin,tmax;
293       curves.push_back(BRep_Tool::CurveOnSurface(e, f, tmin, tmax));
294       cad_edge_t *edg = cad_edge_new(fce, ic, tmin, tmax, curv_fun, curves.back());
295       cad_edge_set_tag(edg, ic);
296       cad_edge_set_property(edg, EDGE_PROPERTY_SOFT_REQUIRED);
297
298       int npts = 0;
299       int ip1, ip2, *ip;
300       gp_Pnt2d e0 = curves.back()->Value(tmin);
301       gp_Pnt ee0 = surfaces.back()->Value(e0.X(), e0.Y());
302       Standard_Real d1=0,d2=0;
303       for (TopExp_Explorer ex_edge(e ,TopAbs_VERTEX); ex_edge.More(); ex_edge.Next()) {
304         TopoDS_Vertex v = TopoDS::Vertex(ex_edge.Current());
305
306         ++npts;
307         if (npts == 1){
308           ip = &ip1;
309           d1 = ee0.SquareDistance(BRep_Tool::Pnt(v));
310         } else {
311           ip = &ip2;
312           d2 = ee0.SquareDistance(BRep_Tool::Pnt(v));
313         }
314         *ip = pmap.FindIndex(v);
315         if(*ip <= 0)
316           *ip = pmap.Add(v);
317       }
318       if (npts != 2) {
319         // should not happen 
320         cout << "An edge does not have 2 extremities." << endl;
321       } else {
322         if (d1 < d2)
323           cad_edge_set_extremities(edg, ip1, ip2);
324         else
325           cad_edge_set_extremities(edg, ip2, ip1);
326       }
327     } // for edge
328   } //for face
329
330
331
332
333   blsurf_session_t *bls = blsurf_session_new(ctx);
334   blsurf_data_set_cad(bls, c);
335
336   SetParameters(_hypothesis, bls);
337
338   cout << endl;
339   cout << "Beginning of Surface Mesh generation" << endl;
340   cout << endl;
341
342   status_t status = STATUS_ERROR;
343
344   try {
345     OCC_CATCH_SIGNALS;
346     status = blsurf_compute_mesh(bls);
347   }
348   catch ( std::exception& exc ) {
349 //     if ( !_comment.empty() )
350 //       _comment += "\n";
351     _comment += exc.what();
352   }
353   catch (Standard_Failure& ex) {
354 //     if ( !_comment.empty() )
355 //       _comment += "\n";
356     _comment += ex.DynamicType()->Name();
357     if ( ex.GetMessageString() && strlen( ex.GetMessageString() )) {
358       _comment += ": ";
359       _comment += ex.GetMessageString();
360     }
361   }
362   catch (...) {
363     if ( _comment.empty() )
364       _comment = "Exception in blsurf_compute_mesh()";
365   }
366   if ( status != STATUS_OK) {
367     blsurf_session_delete(bls);
368     cad_delete(c);
369     context_delete(ctx);
370
371     return error(_comment);
372     //return false;
373   }
374
375   cout << endl;
376   cout << "End of Surface Mesh generation" << endl;
377   cout << endl;
378
379   mesh_t *msh;
380   blsurf_data_get_mesh(bls, &msh);
381   if(!msh){
382     blsurf_session_delete(bls);
383     cad_delete(c);
384     context_delete(ctx);
385     
386     return error(_comment);
387     //return false;
388   }
389   
390   integer nv, ne, nt, nq, vtx[4], tag;
391   real xyz[3];
392
393   mesh_get_vertex_count(msh, &nv);
394   mesh_get_edge_count(msh, &ne);
395   mesh_get_triangle_count(msh, &nt);
396   mesh_get_quadrangle_count(msh, &nq);
397
398   
399   SMESHDS_Mesh* meshDS = aMesh.GetMeshDS();
400   SMDS_MeshNode** nodes = new SMDS_MeshNode*[nv+1];
401   bool* tags = new bool[nv+1];
402
403   for(int iv=1;iv<=nv;iv++) {
404     mesh_get_vertex_coordinates(msh, iv, xyz);
405     mesh_get_vertex_tag(msh, iv, &tag);    
406     nodes[iv] = meshDS->AddNode(xyz[0], xyz[1], xyz[2]);
407     // internal point are tagged to zero
408     if(tag){
409       meshDS->SetNodeOnVertex(nodes[iv], TopoDS::Vertex(pmap(tag)));
410       tags[iv] = false;
411     } else {
412       tags[iv] = true;
413     }
414   }
415
416   for(int it=1;it<=ne;it++) {
417     mesh_get_edge_vertices(msh, it, vtx);
418     SMDS_MeshEdge* edg = meshDS->AddEdge(nodes[vtx[0]], nodes[vtx[1]]);
419     mesh_get_edge_tag(msh, it, &tag);    
420
421     if (tags[vtx[0]]) {
422       meshDS->SetNodeOnEdge(nodes[vtx[0]], TopoDS::Edge(emap(tag)));
423       tags[vtx[0]] = false;
424     };
425     if (tags[vtx[1]]) {
426       meshDS->SetNodeOnEdge(nodes[vtx[1]], TopoDS::Edge(emap(tag)));
427       tags[vtx[1]] = false;
428     };
429     meshDS->SetMeshElementOnShape(edg, TopoDS::Edge(emap(tag)));
430     
431   }
432
433   for(int it=1;it<=nt;it++) {
434     mesh_get_triangle_vertices(msh, it, vtx);
435     SMDS_MeshFace* tri = meshDS->AddFace(nodes[vtx[0]], nodes[vtx[1]], nodes[vtx[2]]);
436     mesh_get_triangle_tag(msh, it, &tag);    
437     meshDS->SetMeshElementOnShape(tri, TopoDS::Face(fmap(tag)));
438     if (tags[vtx[0]]) {
439       meshDS->SetNodeOnFace(nodes[vtx[0]], TopoDS::Face(fmap(tag)));
440       tags[vtx[0]] = false;
441     };
442     if (tags[vtx[1]]) {
443       meshDS->SetNodeOnFace(nodes[vtx[1]], TopoDS::Face(fmap(tag)));
444       tags[vtx[1]] = false;
445     };
446     if (tags[vtx[2]]) {
447       meshDS->SetNodeOnFace(nodes[vtx[2]], TopoDS::Face(fmap(tag)));
448       tags[vtx[2]] = false;
449     };
450   }
451
452   for(int it=1;it<=nq;it++) {
453     mesh_get_quadrangle_vertices(msh, it, vtx);
454     SMDS_MeshFace* quad = meshDS->AddFace(nodes[vtx[0]], nodes[vtx[1]], nodes[vtx[2]], nodes[vtx[3]]);
455     mesh_get_quadrangle_tag(msh, it, &tag);    
456     meshDS->SetMeshElementOnShape(quad, TopoDS::Face(fmap(tag)));
457     if (tags[vtx[0]]) {
458       meshDS->SetNodeOnFace(nodes[vtx[0]], TopoDS::Face(fmap(tag)));
459       tags[vtx[0]] = false;
460     };
461     if (tags[vtx[1]]) {
462       meshDS->SetNodeOnFace(nodes[vtx[1]], TopoDS::Face(fmap(tag)));
463       tags[vtx[1]] = false;
464     };
465     if (tags[vtx[2]]) {
466       meshDS->SetNodeOnFace(nodes[vtx[2]], TopoDS::Face(fmap(tag)));
467       tags[vtx[2]] = false;
468     };
469     if (tags[vtx[3]]) {
470       meshDS->SetNodeOnFace(nodes[vtx[3]], TopoDS::Face(fmap(tag)));
471       tags[vtx[3]] = false;
472     };
473   }
474
475   delete nodes;
476
477   /* release the mesh object */
478   blsurf_data_regain_mesh(bls, msh);
479
480   /* clean up everything */
481   blsurf_session_delete(bls);
482   cad_delete(c);
483
484   context_delete(ctx);
485
486   return true;
487 }
488
489 //=============================================================================
490 /*!
491  *  
492  */
493 //=============================================================================
494
495 ostream & BLSURFPlugin_BLSURF::SaveTo(ostream & save)
496 {
497   return save;
498 }
499
500 //=============================================================================
501 /*!
502  *  
503  */
504 //=============================================================================
505
506 istream & BLSURFPlugin_BLSURF::LoadFrom(istream & load)
507 {
508   return load;
509 }
510
511 //=============================================================================
512 /*!
513  *  
514  */
515 //=============================================================================
516
517 ostream & operator << (ostream & save, BLSURFPlugin_BLSURF & hyp)
518 {
519   return hyp.SaveTo( save );
520 }
521
522 //=============================================================================
523 /*!
524  *  
525  */
526 //=============================================================================
527
528 istream & operator >> (istream & load, BLSURFPlugin_BLSURF & hyp)
529 {
530   return hyp.LoadFrom( load );
531 }
532
533 status_t curv_fun(real t, real *uv, real *dt, real *dtt, void *user_data)
534 {
535   const Geom2d_Curve*pargeo = (const Geom2d_Curve*) user_data;
536
537   if (uv){
538     gp_Pnt2d P;
539     P=pargeo->Value(t);
540     uv[0]=P.X(); uv[1]=P.Y();
541   }
542
543   if(dt) {
544     gp_Vec2d V1;
545     V1=pargeo->DN(t,1);
546     dt[0]=V1.X(); dt[1]=V1.Y();
547   }
548
549   if(dtt){
550     gp_Vec2d V2;
551     V2=pargeo->DN(t,2);
552     dtt[0]=V2.X(); dtt[1]=V2.Y();
553   }
554
555   return 0;
556 }
557
558 status_t surf_fun(real *uv, real *xyz, real*du, real *dv,
559                   real *duu, real *duv, real *dvv, void *user_data)
560 {
561   const Geom_Surface* geometry = (const Geom_Surface*) user_data;
562
563   if(xyz){
564    gp_Pnt P;
565    P=geometry->Value(uv[0],uv[1]);   // S.D0(U,V,P);
566    xyz[0]=P.X(); xyz[1]=P.Y(); xyz[2]=P.Z();
567   }
568
569   if(du && dv){
570     gp_Pnt P;
571     gp_Vec D1U,D1V;
572     
573     geometry->D1(uv[0],uv[1],P,D1U,D1V);
574     du[0]=D1U.X(); du[1]=D1U.Y(); du[2]=D1U.Z();
575     dv[0]=D1V.X(); dv[1]=D1V.Y(); dv[2]=D1V.Z();
576   }
577
578   if(duu && duv && dvv){
579     gp_Pnt P;
580     gp_Vec D1U,D1V;
581     gp_Vec D2U,D2V,D2UV;
582     
583     geometry->D2(uv[0],uv[1],P,D1U,D1V,D2U,D2V,D2UV);
584     duu[0]=D2U.X(); duu[1]=D2U.Y(); duu[2]=D2U.Z();
585     duv[0]=D2UV.X(); duv[1]=D2UV.Y(); duv[2]=D2UV.Z();
586     dvv[0]=D2V.X(); dvv[1]=D2V.Y(); dvv[2]=D2V.Z();    
587   }
588
589   return 0;
590 }
591
592 status_t message_callback(message_t *msg, void *user_data)
593 {
594   integer errnumber = 0;
595   char *desc;
596   message_get_number(msg, &errnumber);
597   message_get_description(msg, &desc);
598   if ( errnumber < 0 ) {
599     string * error = (string*)user_data;
600 //   if ( !error->empty() )
601 //     *error += "\n";
602     // remove ^A from the tail
603     int len = strlen( desc );
604     while (len > 0 && desc[len-1] != '\n')
605       len--;
606     error->append( desc, len );
607   }
608   else {
609     cout << desc;
610   }
611   return STATUS_OK;
612 }