Salome HOME
Copyright update 2020
[modules/paravis.git] / src / PVGUI / PVGUI_DataModel.cxx
1 // Copyright (C) 2010-2020  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 // Author : Adrien Bruneton (CEA)
20 //
21
22 //Local includes
23 #include "PVGUI_DataModel.h"
24 #include "PVGUI_Module.h"
25
26 // GUI includes
27 #include <LightApp_Study.h>
28 #include <LightApp_Module.h>
29 #include <LightApp_Application.h>
30 #include <LightApp_DataModel.h>
31 #include <CAM_DataObject.h>
32 #include <SUIT_Tools.h>
33 #include <SUIT_Session.h>
34 #include <SUIT_ResourceMgr.h>
35
36 // KERNEL
37 #include <utilities.h>
38
39 // Qt includes
40 #include <QFile>
41 #include <QFileInfo>
42 #include <QTextStream>
43 #include <QDomNode>
44
45 // ParaView include
46 #include <pqApplicationCore.h>
47 #include <pqServer.h>
48 #include <vtkOutputWindow.h>
49
50 const QString PVGUI_DataModel::RESTORE_FLAG_FILE = "do_restore_paravis_references.par";
51
52 /*!
53  * XML processing functions to handle the PV state file.
54  */
55 namespace {
56
57   QStringList multiFormats()
58   {
59     static QStringList formats;
60     if ( formats.isEmpty() )
61       formats << "vtm" << "lata" << "pvd";
62     return formats;
63   }
64   void warning( const QString& message )
65   {
66     vtkOutputWindow* vtkWindow = vtkOutputWindow::GetInstance();
67     if ( vtkWindow )
68     {
69       vtkWindow->DisplayWarningText( qPrintable( message ) );
70     }
71   }
72
73   void processElements(QDomNode& thePropertyNode, QStringList& theFileNames,
74                        const QString& theNewPath, bool theRestore)
75   {
76     QDomElement aProperty = thePropertyNode.toElement();
77     int aNbElements = aProperty.attribute("number_of_elements").toInt();
78     if ( aNbElements > 1 )
79       warning( QString("You save data file with number of entities > 1 (%1); some data may be lost!").arg(aNbElements) );
80     QDomNode aElementNode = thePropertyNode.firstChild();
81     while (aElementNode.isElement()) {
82       QDomElement aElement = aElementNode.toElement();
83       if (aElement.tagName() == "Element") {
84         QString aIndex = aElement.attribute("index");
85         if (aIndex == "0") {
86           QString aValue = aElement.attribute("value");
87           if (!aValue.isNull()) {
88             if (theNewPath.isEmpty()) {
89               QFileInfo aFInfo(aValue);
90               if (aFInfo.exists()) {
91                 QString ext = aFInfo.suffix();
92                 if ( multiFormats().contains(aFInfo.suffix()) )
93                   warning( QString("You save data in multiple file format (%1); some data may be not saved!").arg(ext) );
94                 theFileNames<<aValue;
95                 aElement.setAttribute("value", aFInfo.fileName());
96               }
97               break;
98             } else {
99               if (theRestore)
100                 aElement.setAttribute("value", QString(theNewPath) + aValue);
101             }
102           }
103         }
104       }
105       aElementNode = aElementNode.nextSibling();
106     }
107   }
108
109   void processProperties(QDomNode& theProxyNode, QStringList& theFileNames,
110                          const QString& theNewPath, bool theRestore)
111   {
112     QDomNode aPropertyNode = theProxyNode.firstChild();
113     while (aPropertyNode.isElement()) {
114       QDomElement aProperty = aPropertyNode.toElement();
115       QString aName = aProperty.attribute("name");
116       if ((aName == "FileName") || (aName == "FileNameInfo") || (aName == "FileNames")) {
117         processElements(aPropertyNode, theFileNames, theNewPath, theRestore);
118       }
119       aPropertyNode = aPropertyNode.nextSibling();
120     }
121   }
122
123
124   void processProxies(QDomNode& theNode, QStringList& theFileNames,
125                       const QString& theNewPath, bool theRestore)
126   {
127     QDomNode aProxyNode = theNode.firstChild();
128     while (aProxyNode.isElement()) {
129       QDomElement aProxy = aProxyNode.toElement();
130       if (aProxy.tagName() == "Proxy") {
131         QString aGroup = aProxy.attribute("group");
132         if (aGroup == "sources") {
133           processProperties(aProxyNode, theFileNames, theNewPath, theRestore);
134         }
135       }
136       aProxyNode = aProxyNode.nextSibling();
137     }
138   }
139
140   bool processAllFilesInState(const QString& aFileName, QStringList& theFileNames,
141                               const QString& theNewPath, bool theRestore)
142   {
143     QFile aFile(aFileName);
144     if (!aFile.open(QFile::ReadOnly)) {
145       MESSAGE("Can't open state file "<<aFileName.toStdString());
146       return false;
147     }
148     QDomDocument aDoc;
149     bool aRes = aDoc.setContent(&aFile);
150     aFile.close();
151
152     if (!aRes) {
153       MESSAGE("File "<<aFileName.toStdString()<<" is not XML document");
154       return false;
155     }
156
157     QDomElement aRoot = aDoc.documentElement();
158     if ( aRoot.isNull() ) {
159       MESSAGE( "Invalid XML root" );
160       return false;
161     }
162
163     QDomNode aNode = aRoot.firstChild();
164     while (aRes  && !aNode.isNull() ) {
165       aRes = aNode.isElement();
166       if ( aRes ) {
167         QDomElement aSection = aNode.toElement();
168         if (aSection.tagName() == "ServerManagerState") {
169           processProxies(aNode, theFileNames, theNewPath, theRestore);
170         }
171       }
172       aNode = aNode.nextSibling();
173     }
174     if (!aFile.open(QFile::WriteOnly | QFile::Truncate)) {
175       MESSAGE("Can't open state file "<<aFileName.toStdString()<<" for writing");
176       return false;
177     }
178     QTextStream out(&aFile);
179     aDoc.save(out, 2);
180     aFile.close();
181
182     return true;
183   }
184 }
185
186
187 PVGUI_DataModel::PVGUI_DataModel( PVGUI_Module* theModule ):
188   LightApp_DataModel(theModule),
189   myStudyURL("")
190 {}
191
192 PVGUI_DataModel::~PVGUI_DataModel()
193 {}
194
195 bool PVGUI_DataModel::create( CAM_Study* theStudy) {
196   bool res = LightApp_DataModel::create(theStudy);
197   publishComponent(theStudy);
198   return res;
199 }
200
201 void PVGUI_DataModel::publishComponent( CAM_Study* theStudy ) {
202   LightApp_Study* study = dynamic_cast<LightApp_Study*>( theStudy );
203   CAM_ModuleObject *aModelRoot = dynamic_cast<CAM_ModuleObject*>( root());
204   if( study && aModelRoot == NULL ) {
205     aModelRoot = createModuleObject( theStudy->root() );
206     aModelRoot->setDataModel( this );
207     setRoot(aModelRoot);
208   }
209 }
210
211 bool PVGUI_DataModel::dumpPython( const QString& path, CAM_Study* std,
212             bool isMultiFile, QStringList& listOfFiles)
213 {
214
215   LightApp_Study* study = dynamic_cast<LightApp_Study*>( std );
216   if(!study)
217     return false;
218
219   std::string aTmpDir = study->GetTmpDir( path.toLatin1().constData(), isMultiFile );
220   std::string aFile = aTmpDir + "paravis_dump.tmp";
221
222   listOfFiles.append(aTmpDir.c_str());
223   listOfFiles.append("paravis_dump.tmp");
224
225   QFile file(aFile.c_str());
226   if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
227     return false;
228
229   PVGUI_Module * mod = (PVGUI_Module *) getModule();
230   QString trace(mod->getTraceString());
231
232   if (isMultiFile)
233     {
234       QStringList abuffer;
235       abuffer.push_back(QString("def RebuildData():"));
236       QStringList lst(trace.split("\n"));
237       foreach(QString elem, lst)
238         {
239           QString s = "  " + elem;
240           abuffer.push_back(s);
241         }
242       abuffer.push_back(QString("  pass"));
243       trace = abuffer.join("\n");
244     }
245   QTextStream out(&file);
246   out << trace.toStdString().c_str() << "\n";
247   out.flush();
248   file.close();
249
250   return true;
251 }
252
253 /*!
254   \brief Open data model (read ParaView pipeline state from the files).
255   \param theName study file path
256   \param theStudy study pointer
257   \param theList list of the (temporary) files with data
258   \return operation status (\c true on success and \c false on error)
259 */
260 bool PVGUI_DataModel::open( const QString& theName, CAM_Study* theStudy, QStringList theList)
261 {
262   bool ret = false;
263   LightApp_Study* aDoc = dynamic_cast<LightApp_Study*>( theStudy );
264   if ( !aDoc )
265     return false;
266
267   LightApp_DataModel::open( theName, aDoc, theList );
268   publishComponent(theStudy);
269
270   // The first list item contains path to a temporary
271   // directory, where the persistent files was placed
272   if ( theList.count() > 0 ) {
273     QString aTmpDir ( theList[0] );
274
275     if ( theList.size() >= 2 ) {
276       myStudyURL = theName;
277       QString aFullPath = SUIT_Tools::addSlash( aTmpDir ) + theList[1];
278 //      std::cout << "open: tmp dir is" << aFullPath.toStdString() << std::endl;
279       PVGUI_Module * mod = dynamic_cast<PVGUI_Module *>(getModule());
280       if (mod)
281         {
282           bool doRestore = false;
283           QStringList srcFilesEmpty;
284           createAndCheckRestoreFlag(aTmpDir, srcFilesEmpty, /*out*/doRestore);
285           if(doRestore)
286             {
287               // Update state file so that it points to new dir:
288               processAllFilesInState(aFullPath, srcFilesEmpty, aTmpDir.toStdString().c_str(), true);
289             }
290
291           mod->loadParaviewState(aFullPath);
292           ret = true;
293         }
294       ret = true;
295     }
296   }
297
298   return ret;
299 }
300
301 /*!
302  * Create an empty file indicating whether source files in the pipeline should be restored.
303  */
304 bool PVGUI_DataModel::createAndCheckRestoreFlag(const QString& tmpdir, QStringList& listOfFiles, bool & alreadyThere)
305 {
306   QString aFullPath = SUIT_Tools::addSlash( tmpdir ) + RESTORE_FLAG_FILE;
307   QFile f(aFullPath);
308   if (f.exists())
309     {
310     alreadyThere = true;
311     return true;
312     }
313   else
314     {
315       bool ret = f.open(QFile::WriteOnly);
316       if (ret)
317         {
318         f.close();
319         listOfFiles << RESTORE_FLAG_FILE;
320         }
321       return ret;
322     }
323 }
324
325
326 /*!
327   \brief Save data model (write ParaView pipeline to the files).
328   \param listOfFiles returning list of the (temporary) files with saved data
329   \return operation status (\c true on success and \c false on error)
330 */
331 bool PVGUI_DataModel::save( QStringList& theListOfFiles)
332 {
333   bool isMultiFile = false; // TODO: decide, how to access this parameter
334   bool ret = false;
335
336   LightApp_DataModel::save( theListOfFiles );
337
338   LightApp_Study* study = dynamic_cast<LightApp_Study*>( getModule()->getApp()->activeStudy() );
339   QString aTmpDir = study->GetTmpDir( myStudyURL.toLatin1(), isMultiFile ).c_str();
340 //  std::cout << "save: tmp dir is" << aTmpDir.toStdString() << std::endl;
341
342   QString aFileName = SUIT_Tools::file( myStudyURL, false ) + "_PARAVIS.pvsm";
343   QString aFullPath = aTmpDir + aFileName;
344
345   PVGUI_Module * mod = dynamic_cast<PVGUI_Module *>(getModule());
346   QStringList srcFiles;
347   if (mod)
348     {
349       // Create ParaView state file:
350       mod->saveParaviewState(aFullPath.toStdString().c_str());
351
352       // add this to the list to be saved:
353       theListOfFiles << aTmpDir;
354       theListOfFiles << aFileName;
355
356       // Potentially save referenced files:
357       SUIT_ResourceMgr* aResourceMgr = SUIT_Session::session()->resourceMgr();
358       int aSavingType = aResourceMgr->integerValue( "PARAVIS", "savestate_type", 0 );
359
360       bool unused;
361       bool isBuiltIn = false;
362       pqServer* aServer;
363       QString nullS;
364
365       switch (aSavingType) {
366         case 0: // Save referenced files when they are accessible
367           createAndCheckRestoreFlag(aTmpDir, theListOfFiles ,unused);
368           processAllFilesInState(aFullPath, srcFiles, nullS, false);
369           break;
370         case 1: // Save referenced files only if this is the builtin server
371           aServer = pqApplicationCore::instance()->getActiveServer();
372           if (aServer)
373             isBuiltIn = !aServer->isRemote();
374           if(isBuiltIn)
375             {
376               createAndCheckRestoreFlag(aTmpDir, theListOfFiles, unused);
377               processAllFilesInState(aFullPath, srcFiles, nullS, false);
378             }
379           break;
380         case 2: // Do not save referenced file
381           break;
382         default:
383           break;
384       }
385
386       ret = true;
387     }
388   // Copying valid source files to the temp directory and adding them to the list
389   foreach(QString fName, srcFiles)
390   {
391     QFile fSrc(fName);
392     if (fSrc.exists())
393       {
394         QFileInfo inf(fSrc);
395         QString newPth(SUIT_Tools::addSlash( aTmpDir ) + inf.fileName());
396         if (fSrc.copy(newPth))
397           {
398             theListOfFiles << inf.fileName();
399           }
400       }
401   }
402
403   return ret;
404 }
405
406 /*!
407   \brief Save data model (write ParaView pipeline state to the files).
408   \param url study file path
409   \param study study pointer
410   \param listOfFiles returning list of the (temporary) files with saved data
411   \return operation status (\c true on success and \c false on error)
412 */
413 bool PVGUI_DataModel::saveAs( const QString& url, CAM_Study* study, QStringList& theListOfFiles)
414 {
415   myStudyURL = url;
416   return save( theListOfFiles );
417 }