]> SALOME platform Git repositories - modules/homard.git/blob - src/HOMARDGUI/HOMARDGUI.cxx
Salome HOME
integration of modifications from Gerald Nicolas
[modules/homard.git] / src / HOMARDGUI / HOMARDGUI.cxx
1 // Copyright (C) 2011-2012  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.
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
20 //  File   : HOMARDGUI.cxx
21 //  Author : Paul RASCLE, EDF
22 //  Module : HOMARD
23
24 using namespace std;
25 #include "HOMARDGUI.h"
26
27 // SALOME Includes
28 #include "Utils_ORB_INIT.hxx"
29 #include "Utils_SINGLETON.hxx"
30 #include "SALOME_LifeCycleCORBA.hxx"
31
32 #include "SUIT_ResourceMgr.h"
33 #include "SUIT_MessageBox.h"
34 #include "SUIT_Session.h"
35 #include "SUIT_ViewWindow.h"
36 #include "SUIT_ViewManager.h"
37 #include <SUIT_Desktop.h>
38
39 #include "CAM_Module.h"
40 #include "OB_Browser.h"
41
42 #include "SALOME_ListIO.hxx"
43 #include "SALOME_ListIteratorOfListIO.hxx"
44
45 #include "SalomeApp_Application.h"
46 #include "SalomeApp_DataModel.h"
47 #include "SalomeApp_Study.h"
48 #include "LightApp_SelectionMgr.h"
49 #include "LightApp_Selection.h"
50 #include <LightApp_Preferences.h>
51 #include "SalomeApp_Module.h"
52 #include "SALOMEconfig.h"
53 #include <SALOME_LifeCycleCORBA.hxx>
54
55 #include <utilities.h>
56
57
58 // QT Includes
59 #include <QMenu>
60 #include "MonCreateCase.h"
61 #include "MonCreateIteration.h"
62 #include "MonEditFile.h"
63 #include "MonEditCase.h"
64 #include "MonEditIteration.h"
65 #include "MonEditHypothesis.h"
66 #include "MonEditZone.h"
67 #include "MonEditBoundaryAn.h"
68 #include "MonEditBoundaryDi.h"
69 #include "HomardQtCommun.h"
70
71 // BOOST Includes
72 #include <boost/shared_ptr.hpp>
73
74 //Pour le _CAST
75 #include "SALOMEDS_Study.hxx"
76 #include "HOMARDGUI_Utils.h"
77
78 static CORBA::ORB_var _orb;
79
80 //=======================================================================
81 // function : HOMARDGUI()
82 // purpose  : Constructor
83 //=======================================================================
84 HOMARDGUI::HOMARDGUI(const QString&) :
85            SalomeApp_Module( "HOMARD" ), // default name
86            LightApp_Module( "HOMARD" )
87 {
88 }
89
90
91 //=======================================================================
92 // function : ~HOMARDGUI()
93 // purpose  : Destructor
94 //=======================================================================
95 HOMARDGUI::~HOMARDGUI()
96 {
97 }
98
99 //=======================================================================
100 // function : InitHOMARDGen
101 // launch HOMARD component and return a handle
102 //=======================================================================
103 HOMARD::HOMARD_Gen_var HOMARDGUI::InitHOMARDGen(SalomeApp_Application* app)
104 {
105   Engines::EngineComponent_var comp = app->lcc()->FindOrLoad_Component( "FactoryServer","HOMARD" );
106   HOMARD::HOMARD_Gen_var clr = HOMARD::HOMARD_Gen::_narrow(comp);
107   ASSERT(!CORBA::is_nil(clr));
108   return clr;
109 }
110
111 //=======================================================================
112 // Module's initialization
113 void HOMARDGUI::initialize( CAM_Application* app )
114 //=======================================================================
115 {
116   SalomeApp_Module::initialize( app );
117   InitHOMARDGen(dynamic_cast<SalomeApp_Application*>( app ));
118   anId = 0;
119   createActions();
120   createMenus();
121 }
122
123 //================================================
124 // function : createHOMARDAction
125 // create an item in status bar and Homard menu
126 //================================================
127 void HOMARDGUI::createHOMARDAction( const int id, const QString& po_id, const QString& icon_id, const int key, const bool toggle  )
128 {
129 //   MESSAGE("createHOMARDAction");
130   QIcon icon;
131   QWidget* parent = application()->desktop();
132   SUIT_ResourceMgr* resMgr = application()->resourceMgr();
133   QPixmap pix;
134   if ( icon_id.length() )
135      pix = resMgr->loadPixmap( "HOMARD", tr( icon_id .toLatin1().data()) );
136   else
137      pix = resMgr->loadPixmap( "HOMARD", tr( QString( "ICO_%1" ).arg( po_id ).toLatin1().data()), false );
138   if ( !pix.isNull() )
139       icon = QIcon( pix );
140
141   QString tooltip    = tr(QString( "HOM_TOP_%1" ).arg( po_id ).toLatin1().data()),
142           menu       = tr(QString( "HOM_MEN_%1" ).arg( po_id ).toLatin1().data()),
143           status_bar = tr(QString( "HOM_STB_%1" ).arg( po_id ).toLatin1().data());
144
145   createAction( id, tooltip, icon, menu, status_bar, key, parent, toggle, this, SLOT( OnGUIEvent() )  );
146 }
147
148 //================================================
149 // function : createAction
150 // constructs Homard menu
151 // calls createHOMARDAction for each item
152 //================================================
153 void HOMARDGUI::createActions(){
154 //
155   createHOMARDAction( 1101, "NEW_CASE",       "cas_calcule.png"  );
156   createHOMARDAction( 1102, "NEW_ITERATION",  "iter_next.png"    );
157   createHOMARDAction( 1111, "COMPUTE",        "mesh_compute.png" );
158 //
159   createHOMARDAction( 1201, "EDIT",           "whatis.png"       );
160   createHOMARDAction( 1211, "DELETE",         "delete.png"       );
161 //
162   createHOMARDAction( 1301, "EDIT_MESS_FILE", "texte.png"        );
163 //
164 }
165
166 //================================================
167 // function : createPreferences
168 // No preferences for Homard
169 // Just a test
170 //================================================
171 void HOMARDGUI::createPreferences(){
172    QString toto = tr( "PREF_GROUP_GENERAL" );
173    int tabId  = addPreference( tr( "PREF_GROUP_GENERAL" ) );
174    int genGroup = addPreference( tr( "PREF_TAB_SETTINGS" ), tabId );
175    addPreference( tr( "PREF_TEST" ), genGroup, LightApp_Preferences::Color, "Homard", "shading_color" );
176 }
177
178
179 //================================================
180 // function : createMenus
181 //                Verifier l'avant dernier nombre passe en parametre
182 //================================================
183 void HOMARDGUI::createMenus(){
184   MESSAGE("createMenus")
185 //
186   int HOMARD_Id  = createMenu( tr( "HOM_MEN_HOMARD" ),  -1,  5, 10 );
187   createMenu( 1101, HOMARD_Id, -1 ); //Create_Case
188   createMenu( 1102, HOMARD_Id, -1 ); //Create_Iteration
189   createMenu( 1111, HOMARD_Id, -1 ); //COMPUTE
190 //
191   HOMARD_Id  = createMenu( tr( "HOM_MEN_MODIFICATION" ),  -1,  5, 10 );
192   createMenu( 1201, HOMARD_Id, -1 ); //Edit
193   createMenu( 1211, HOMARD_Id, -1 ); //Delete
194 //
195   HOMARD_Id  = createMenu( tr( "HOM_MEN_INFORMATION" ),  -1,  5, 10 );
196   createMenu( 1301, HOMARD_Id, -1 ); //EditAsciiFile pour le fichier listeStd ou bilan
197   createMenu( separator(), HOMARD_Id,-1);
198   createMenu( 1201, HOMARD_Id, -1 ); //Edit
199   createMenu( separator(), HOMARD_Id,-1);
200 //   createMenu( 1201, HOMARD_Id, -1 ); //Edit
201 }
202
203 //================================================
204 void HOMARDGUI::OnGUIEvent()
205 //================================================
206 {
207   MESSAGE("OnGUIEvent()")
208   setOrb();
209   const QObject* obj = sender();
210   if ( !obj || !obj->inherits( "QAction" ) )
211        return;
212   int id = actionId((QAction*)obj);
213   bool ret;
214   if ( id != -1 )
215       ret = OnGUIEvent( id );
216   MESSAGE("************** End of OnGUIEvent()");
217 }
218
219 //=======================================================================
220 // Method OnGUIEvent pour Homard
221 //=======================================================================
222 bool HOMARDGUI::OnGUIEvent (int theCommandID)
223 {
224   MESSAGE("OnGUIEvent(int)");
225   SalomeApp_Application* app = dynamic_cast< SalomeApp_Application* >( application() );
226   if ( !app ) return false;
227
228   SalomeApp_Study* stud = dynamic_cast<SalomeApp_Study*> ( app->activeStudy() );
229   if ( !stud ) {
230     MESSAGE ( "FAILED to cast active study to SalomeApp_Study" );
231     return false;
232   }
233
234   SUIT_Desktop* parent = application()->desktop();
235   HOMARD::HOMARD_Gen_var homardGen = HOMARDGUI::InitHOMARDGen(app);
236
237   if (!CORBA::is_nil(homardGen))
238   {
239     // Set current study
240     SalomeApp_Study* aSAStudy =dynamic_cast<SalomeApp_Study*>(app->activeStudy());
241     _PTR(Study) aStudy = aSAStudy->studyDS();
242     SALOMEDS::Study_ptr aStudyDS;
243     if (aStudy)
244       aStudyDS = _CAST(Study,aStudy)->GetStudy();
245       homardGen->SetCurrentStudy(aStudyDS);
246    }
247   getApp()->updateObjectBrowser();
248
249   SCRUTE(theCommandID);
250   switch (theCommandID)
251   {
252     case 1101: // Creation d un Cas
253     {
254       MESSAGE("etape 1101")
255       MESSAGE("command " << theCommandID << " activated");
256       MonCreateCase *aDlg = new MonCreateCase( parent, TRUE,
257                             HOMARD::HOMARD_Gen::_duplicate(homardGen) ) ;
258       aDlg->show();
259       break;
260     }
261
262     case 1102: // Creation d une Iteration
263     {
264       MESSAGE("command " << theCommandID << " activated");
265       QString IterParentName=HOMARD_QT_COMMUN::SelectionArbreEtude(QString("IterationHomard"), 0);
266       MESSAGE("IterParentName " << IterParentName.toStdString().c_str() << " choisi dans arbre");
267       MonCreateIteration *IterDlg = new MonCreateIteration( parent, true,
268                                      HOMARD::HOMARD_Gen::_duplicate(homardGen), IterParentName ) ;
269       IterDlg->show();
270       break;
271     }
272
273     case 1111: // Compute une Iteration
274     {
275       MESSAGE("command " << theCommandID << " activated");
276       QString monIter=HOMARD_QT_COMMUN::SelectionArbreEtude(QString("IterationHomard"), 1);
277       if (monIter == QString("")) break;
278       try
279       {
280         homardGen->Compute(monIter.toStdString().c_str(), 0);
281       }
282       catch( SALOME::SALOME_Exception& S_ex )
283       {
284         QMessageBox::critical( 0, QObject::tr("HOM_ERROR"),
285                                   QString(CORBA::string_dup(S_ex.details.text)) );
286         getApp()->updateObjectBrowser();
287         return false;
288       }
289       break;
290     }
291
292     case 1201: // Edition d'un objet
293     {
294       MESSAGE("command " << theCommandID << " activated");
295       QString nomObjet = HOMARD_QT_COMMUN::SelectionArbreEtude(QString(""), 1);
296       if (nomObjet == QString("")) break;
297       _PTR(SObject) obj = chercheMonObjet();
298       if (obj)
299       {
300         // Edition d'un cas
301         if (HOMARD_UTILS::isCase(obj))
302         {
303           MonEditCase *aDlg = new MonEditCase(parent, true, HOMARD::HOMARD_Gen::_duplicate(homardGen), _ObjectName ) ;
304           aDlg->show();
305         }
306         // Edition d'une iteration
307         else if (HOMARD_UTILS::isIter(obj))
308         {
309           MonEditIteration *aDlg = new MonEditIteration(parent, true, HOMARD::HOMARD_Gen::_duplicate(homardGen), QString(""), _ObjectName ) ;
310           aDlg->show();
311         }
312         // Edition d'une hypothese
313         else if (HOMARD_UTILS::isHypo(obj))
314         {
315           MonEditHypothesis *aDlg = new MonEditHypothesis(0, true, HOMARD::HOMARD_Gen::_duplicate(homardGen),  _ObjectName, QString(""), QString("")) ;
316           aDlg->show();
317         }
318         // Edition d'une zone
319         else if (HOMARD_UTILS::isZone(obj))
320         {
321           MonEditZone *aDlg = new MonEditZone(0, TRUE, HOMARD::HOMARD_Gen::_duplicate(homardGen), QString(""), _ObjectName ) ;
322           aDlg->show();
323         }
324         // Edition d'une frontiere discrete
325         else if (HOMARD_UTILS::isBoundaryDi(obj))
326         {
327             MESSAGE(".. Lancement de MonEditBoundaryDi" );
328             MonEditBoundaryDi *aDlg = new MonEditBoundaryDi(0, TRUE, HOMARD::HOMARD_Gen::_duplicate(homardGen), QString(""), _ObjectName ) ;
329             aDlg->show();
330         }
331         // Edition d'une frontiere analytique
332         else if (HOMARD_UTILS::isBoundaryAn(obj))
333         {
334             MESSAGE(".. Lancement de MonEditBoundaryAn" );
335             MonEditBoundaryAn *aDlg = new MonEditBoundaryAn(0, TRUE, HOMARD::HOMARD_Gen::_duplicate(homardGen), QString(""), _ObjectName ) ;
336             aDlg->show();
337         }
338       }
339       break;
340     }
341
342     case 1211: // Suppression d'un objet
343     {
344       MESSAGE("command " << theCommandID << " activated");
345       QString nomObjet = HOMARD_QT_COMMUN::SelectionArbreEtude(QString(""), 1);
346       if (nomObjet == QString("")) break;
347       _PTR(SObject) obj = chercheMonObjet();
348       if (obj)
349       {
350         // Suppression d'un cas
351         if (HOMARD_UTILS::isCase(obj))
352         {
353           try
354           { homardGen->DeleteCase(_ObjectName.toStdString().c_str()); }
355           catch( SALOME::SALOME_Exception& S_ex )
356           {
357             QMessageBox::critical( 0, QObject::tr("HOM_ERROR"),
358                                       QString(CORBA::string_dup(S_ex.details.text)) );
359             getApp()->updateObjectBrowser();
360             return false;
361           }
362         }
363         // Suppression d'une iteration
364         else if (HOMARD_UTILS::isIter(obj))
365         {
366           try
367           { homardGen->DeleteIteration(_ObjectName.toStdString().c_str()); }
368           catch( SALOME::SALOME_Exception& S_ex )
369           {
370             QMessageBox::critical( 0, QObject::tr("HOM_ERROR"),
371                                       QString(CORBA::string_dup(S_ex.details.text)) );
372             getApp()->updateObjectBrowser();
373             return false;
374           }
375         }
376         // Suppression d'une hypothese
377         else if (HOMARD_UTILS::isHypo(obj))
378         {
379           try
380           { homardGen->DeleteHypo(_ObjectName.toStdString().c_str()); }
381           catch( SALOME::SALOME_Exception& S_ex )
382           {
383             QMessageBox::critical( 0, QObject::tr("HOM_ERROR"),
384                                       QString(CORBA::string_dup(S_ex.details.text)) );
385             getApp()->updateObjectBrowser();
386             return false;
387           }
388         }
389         // Suppression d'une zone
390         else if (HOMARD_UTILS::isZone(obj))
391         {
392           try
393           { homardGen->DeleteZone(_ObjectName.toStdString().c_str()); }
394           catch( SALOME::SALOME_Exception& S_ex )
395           {
396             QMessageBox::critical( 0, QObject::tr("HOM_ERROR"),
397                                       QString(CORBA::string_dup(S_ex.details.text)) );
398             getApp()->updateObjectBrowser();
399             return false;
400           }
401         }
402         // Suppression d'une frontiere
403         else if ( HOMARD_UTILS::isBoundaryDi(obj) or HOMARD_UTILS::isBoundaryAn(obj) )
404         {
405           try
406           { homardGen->DeleteBoundary(_ObjectName.toStdString().c_str()); }
407           catch( SALOME::SALOME_Exception& S_ex )
408           {
409             QMessageBox::critical( 0, QObject::tr("HOM_ERROR"),
410                                       QString(CORBA::string_dup(S_ex.details.text)) );
411             getApp()->updateObjectBrowser();
412             return false;
413           }
414         }
415       }
416       break;
417     }
418
419     case 1301: // Affichage du fichier mess
420     {
421       MESSAGE("command " << theCommandID << " activated avec objet " << _ObjectName.toStdString().c_str() );
422       _PTR(SObject) obj = chercheMonObjet();
423       if ((obj) and ((HOMARD_UTILS::isFileMess(obj) or HOMARD_UTILS::isFileSummary(obj))))
424       {
425           MonEditFile *aDlg = new MonEditFile( 0, true, HOMARD::HOMARD_Gen::_duplicate(homardGen), _ObjectName ) ;
426           aDlg->show();
427       }
428       break;
429      }
430
431   }
432   getApp()->updateObjectBrowser();
433   return true;
434 }
435
436 //=============================================================================
437 /*!
438  *
439  */
440 //=============================================================================
441
442 // Module's engine IOR
443 //=============================================================================
444 QString HOMARDGUI::engineIOR() const
445 //=============================================================================
446 {
447   CORBA::String_var anIOR = getApp()->orb()->object_to_string( InitHOMARDGen(getApp()) );
448   return QString( anIOR.in() );
449 }
450
451 // Module's activation
452 //=============================================================================
453 bool HOMARDGUI::activateModule( SUIT_Study* theStudy )
454 //=============================================================================
455 {
456   bool bOk = SalomeApp_Module::activateModule( theStudy );
457
458   setMenuShown( true );
459   setToolShown( true );
460
461   return bOk;
462 }
463
464 // Module's deactivation
465 //=============================================================================
466 bool HOMARDGUI::deactivateModule( SUIT_Study* theStudy )
467 //=============================================================================
468 {
469   setMenuShown( false );
470   setToolShown( false );
471
472   return SalomeApp_Module::deactivateModule( theStudy );
473 }
474
475 // Default windows
476 //=============================================================================
477 void HOMARDGUI::windows( QMap<int, int>& theMap ) const
478 //=============================================================================
479 {
480   theMap.clear();
481   theMap.insert( SalomeApp_Application::WT_ObjectBrowser, Qt::LeftDockWidgetArea );
482   theMap.insert( SalomeApp_Application::WT_PyConsole,     Qt::BottomDockWidgetArea );
483 }
484
485 //=============================================================================
486 void HOMARDGUI::setOrb()
487 //=============================================================================
488 {
489   try
490   {
491      ORB_INIT &init = *SINGLETON_<ORB_INIT>::Instance();
492      ASSERT(SINGLETON_<ORB_INIT>::IsAlreadyExisting());
493      _orb = init( 0 , 0 );
494   }
495   catch (...)
496   {
497      INFOS("internal error : orb not found");
498      _orb = 0;
499   }
500             ASSERT(! CORBA::is_nil(_orb));
501 }
502 //========================================
503 _PTR(SObject) HOMARDGUI::chercheMonObjet()
504 //========================================
505 {
506
507     SALOMEDSClient_SObject* aSO = NULL;
508     _PTR(SObject) obj;
509     SALOME_ListIO lst;
510     getApp()->selectionMgr()->selectedObjects( lst );
511     if (  lst.Extent() == 1 )
512     {
513         Handle(SALOME_InteractiveObject) io = lst.First();
514         SalomeApp_Study* appStudy = dynamic_cast<SalomeApp_Study*>( application()->activeStudy() );
515         _PTR(Study) study = appStudy->studyDS();
516         _PTR(SObject) obj = study->FindObjectID( io->getEntry() );
517         _ObjectName = QString( obj->GetName().c_str() );
518         return obj;
519      }
520      else
521          return _PTR(SObject)(aSO);
522 }
523 //=============================================================================
524 void HOMARDGUI::contextMenuPopup( const QString& client, QMenu* menu, QString& title )
525 //=============================================================================
526 {
527   MESSAGE("Debut de contextMenuPopup");
528   _PTR(SObject) obj = chercheMonObjet();
529   if ( obj )
530   {
531     title = QString( obj->GetName().c_str() );
532     _ObjectName = title;
533     SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
534 //
535     QPixmap pix ;
536     bool DeleteObject = false ;
537     bool EditObject = false ;
538 //
539     if ( HOMARD_UTILS::isBoundaryAn(obj) )
540     {
541       EditObject = true ;
542       DeleteObject = true ;
543     }
544     else if ( HOMARD_UTILS::isBoundaryDi(obj) )
545     {
546       EditObject = true ;
547       DeleteObject = true ;
548     }
549     else if ( HOMARD_UTILS::isZone(obj) )
550     {
551       EditObject = true ;
552       DeleteObject = true ;
553     }
554     else if ( HOMARD_UTILS::isHypo(obj) )
555     {
556       EditObject = true ;
557       DeleteObject = true ;
558     }
559     else if ( HOMARD_UTILS::isIter(obj) )
560     {
561       pix = resMgr->loadPixmap( "HOMARD", "iter_next.png" );
562       menu->addAction(QIcon(pix), tr(QString("HOM_MEN_NEW_ITERATION").toLatin1().data()), this,SLOT(NextIter()));
563       QPixmap pix2 = resMgr->loadPixmap( "HOMARD", "mesh_compute.png" );
564       menu->addAction(QIcon(pix2), tr(QString("HOM_MEN_COMPUTE").toLatin1().data()), this,SLOT(LanceCalcul()));
565       EditObject = true ;
566       DeleteObject = true ;
567     }
568     else if ( HOMARD_UTILS::isCase(obj) )
569     {
570       EditObject = true ;
571       DeleteObject = true ;
572     }
573     else if ( HOMARD_UTILS::isFileMess(obj) or HOMARD_UTILS::isFileSummary(obj) )
574     {
575       pix = resMgr->loadPixmap( "HOMARD", "texte.png" );
576       menu->addAction(QIcon(pix), tr(QString("HOM_MEN_EDIT_MESS_FILE").toLatin1().data()), this,SLOT(EditAsciiFile()));
577     }
578 //  Ajout d'un menu d'edition pour les objets qui le proposent
579     if ( EditObject )
580     {
581       pix = resMgr->loadPixmap( "HOMARD", "whatis.png" );
582       menu->addAction(QIcon(pix), tr(QString("HOM_MEN_EDIT").toLatin1().data()), this,SLOT(Edit()));
583     }
584 //  Ajout d'un menu de destruction pour les objets qui le proposent
585     if ( DeleteObject )
586     {
587       pix = resMgr->loadPixmap( "HOMARD", "delete.png" );
588       menu->addAction(QIcon(pix), tr(QString("HOM_MEN_DELETE").toLatin1().data()), this,SLOT(Delete()));
589     }
590   }
591 }
592
593 void HOMARDGUI::NextIter()
594 {
595   this->OnGUIEvent(1102);
596 }
597
598 void HOMARDGUI::LanceCalcul()
599 {
600   this->OnGUIEvent(1111);
601 }
602
603 void HOMARDGUI::Edit()
604 {
605   this->OnGUIEvent(1201);
606 }
607
608 void HOMARDGUI::Delete()
609 {
610   this->OnGUIEvent(1211);
611 }
612
613 void HOMARDGUI::EditAsciiFile()
614 {
615   this->OnGUIEvent(1301);
616 }
617
618 //
619 //=============================================================================
620 // Export the module
621 //=============================================================================
622 extern "C" {
623   Standard_EXPORT CAM_Module* createModule()
624   {
625     return new HOMARDGUI("");
626   }
627 }
628