//OpenSCADA module UI.VISION file: tvision.cpp /*************************************************************************** * Copyright (C) 2006-2024 by Roman Savochenko (roman@oscada.org) * 2005-2006 by Evgen Zaichuk * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ***************************************************************************/ #include #include #include #include #include #include #include #include "vis_run.h" #include "vis_shapes.h" #include "vis_shape_elfig.h" #include "vis_widgs.h" #include "vis_devel.h" #if QT_VERSION < 0x050000 # include #endif //************************************************* //* Modul info! * #define MOD_ID "Vision" #define MOD_NAME trS("Operation user interface (Qt)") #define MOD_TYPE SUI_ID #define VER_TYPE SUI_VER #define SUB_TYPE "Qt" #define MOD_VER "9.3.1" #define AUTHORS trS("Roman Savochenko, Maxim Lysenko (2006-2012), Kseniya Yashina (2006-2007), Evgen Zaichuk (2005-2006)") #define DESCRIPTION trS("Visual operation user interface, based on the Qt library - front-end to the VCA engine.") #define LICENSE "GPL2" //************************************************* VISION::TVision *VISION::mod; extern "C" { #ifdef MOD_INCL TModule::SAt ui_Vision_module( int n_mod ) #else TModule::SAt module( int n_mod ) #endif { if(n_mod == 0) return TModule::SAt(MOD_ID,MOD_TYPE,VER_TYPE); return TModule::SAt(""); } #ifdef MOD_INCL TModule *ui_Vision_attach( const TModule::SAt &AtMod, const string &source ) #else TModule *attach( const TModule::SAt &AtMod, const string &source ) #endif { if(AtMod == TModule::SAt(MOD_ID,MOD_TYPE,VER_TYPE)) return new VISION::TVision( source ); return NULL; } } using namespace VISION; //************************************************* //* QTCFG::TVision * //************************************************* TVision::TVision( string name ) : TUI(MOD_ID), mVCAStation(dataRes()), mUserStart(dataRes()), mUserPass(dataRes()), mExitLstRunPrjCls(DEF_ExitLstRunPrjCls), mDropCommonWdgStls(DEF_DropCommonWdgStls), mEndRun(false), mRestTime(DEF_RestoreTime), mCachePgLife(DEF_CachePgLife), mCachePgSz(DEF_CachePgSz), mScrnCnt(0) { mVCAStation = "."; mod = this; modInfoMainSet(MOD_NAME, MOD_TYPE, MOD_VER, AUTHORS, DESCRIPTION, LICENSE, name); //Export functions modFuncReg(new ExpFunc("QIcon icon();","Module Qt-icon",(void(TModule::*)( )) &TVision::icon)); modFuncReg(new ExpFunc("QMainWindow *openWindow();","Start Qt GUI.",(void(TModule::*)( )) &TVision::openWindow)); } TVision::~TVision( ) { //Free widget's shapes for(unsigned iSw = 0; iSw < shapesWdg.size(); iSw++) delete shapesWdg[iSw]; //shapesWdg[iSw]->deleteLater(); //!!!! Goes to crashes at the module disconnection shapesWdg.clear(); if(!SYS->stopSignal()) TSYS::sysSleep(5); } string TVision::optDescr( ) { return TSYS::strMess(_( "======================= Module <%s:%s> options =======================\n" "---- Parameters of the module section '%s' of the configuration file ----\n" "StartUser Start-up, no-password, user.\n" "UserPass User password for non-local start.\n" "RunPrjs List of projects to be launched at the start of the module.\n" "ExitLstRunPrjCls <0|1> Exit closing the last completed project (by default 1).\n" "DropCommonWdgStls <0|1> Reset widget styles to common for some specific widgets in runtime, like to buttons (default 1).\n" "CachePgLife Lifetime of the pages in the cache (by default 1).\n" "CachePgSz Maximum number of the pages in the cache (by default 10).\n" "VCAstation The station with the VCA engine ('.' is local).\n" "RestoreTime Connection recovery time.\n\n"), MOD_TYPE, MOD_ID, nodePath().c_str()); } void TVision::load_( ) { mess_debug(nodePath().c_str(), _("Loading the module.")); //Load parameters from command line //Load parameters from config-file and DB setUserStart(TBDS::genPrmGet(nodePath()+"StartUser")); setUserPass(TBDS::genPrmGet(nodePath()+"UserPass")); setRunPrjs(TBDS::genPrmGet(nodePath()+"RunPrjs")); setExitLstRunPrjCls(s2i(TBDS::genPrmGet(nodePath()+"ExitLstRunPrjCls",i2s(DEF_ExitLstRunPrjCls)))); setDropCommonWdgStls(s2i(TBDS::genPrmGet(nodePath()+"DropCommonWdgStls",i2s(DEF_DropCommonWdgStls)))); setCachePgLife(s2r(TBDS::genPrmGet(nodePath()+"CachePgLife",r2s(DEF_CachePgLife)))); setCachePgSz(s2i(TBDS::genPrmGet(nodePath()+"CachePgSz",i2s(DEF_CachePgSz)))); setVCAStation(TBDS::genPrmGet(nodePath()+"VCAstation",DEF_VCAstation)); setRestoreTime(s2i(TBDS::genPrmGet(nodePath()+"RestoreTime",i2s(DEF_RestoreTime)))); } void TVision::save_( ) { mess_debug(nodePath().c_str(),_("Saving the module.")); //Save parameters to DB TBDS::genPrmSet(nodePath()+"StartUser", userStart()); TBDS::genPrmSet(nodePath()+"UserPass", userPass()); TBDS::genPrmSet(nodePath()+"RunPrjs", runPrjs()); TBDS::genPrmSet(nodePath()+"ExitLstRunPrjCls", i2s(exitLstRunPrjCls())); TBDS::genPrmSet(nodePath()+"DropCommonWdgStls", i2s(dropCommonWdgStls())); TBDS::genPrmSet(nodePath()+"CachePgLife", r2s(cachePgLife())); TBDS::genPrmSet(nodePath()+"CachePgSz", i2s(cachePgSz())); TBDS::genPrmSet(nodePath()+"VCAstation", VCAStation()); TBDS::genPrmSet(nodePath()+"RestoreTime",i2s(restoreTime())); } void TVision::postEnable( int flag ) { TModule::postEnable(flag); } string TVision::uiPropGet( const string &prop, const string &user ) { MtxAlloc res(mnWindsRes, true); XMLNode prmNd; try { prmNd.load(TBDS::genPrmGet(nodePath()+"uiProps","",user)); return prmNd.attr(prop); } catch(TError &err) { } return ""; } void TVision::uiPropSet( const string &prop, const string &vl, const string &user ) { MtxAlloc res(mnWindsRes, true); XMLNode prmNd("UI"); try { prmNd.load(TBDS::genPrmGet(nodePath()+"uiProps","",user)); prmNd.setAttr(prop, vl); TBDS::genPrmSet(nodePath()+"uiProps", prmNd.save(XMLNode::BrAllPast), user); } catch(TError &err) { } } QIcon TVision::icon( ) { QImage ico_t; if(!ico_t.load(TUIS::icoGet("UI.Vision",NULL,true).c_str())) ico_t.load(":/images/vision.png"); return QPixmap::fromImage(ico_t); } QMainWindow *TVision::openWindow( ) { //Getting the allowed screens count #if QT_VERSION >= 0x050000 mScrnCnt = QApplication::screens().size(); #elif QT_VERSION >= 0x040600 mScrnCnt = QDesktopWidget().screenCount(); #endif //Register support widget's shapes if(shapesWdg.empty()) { shapesWdg.push_back(new ShapeElFigure); shapesWdg.push_back(new ShapeFormEl); shapesWdg.push_back(new ShapeText); shapesWdg.push_back(new ShapeMedia); shapesWdg.push_back(new ShapeDiagram); shapesWdg.push_back(new ShapeProtocol); shapesWdg.push_back(new ShapeDocument); shapesWdg.push_back(new ShapeBox); shapesWdg.push_back(new ShapeFunction); } string VCAStat = VCAStation(); string user_open = userStart(); string user_pass = userPass(); //Checking for true setting the starting user and asking the remote station int err = 0; XMLNode req("get"); req.setAttr("path", "/%2fgen%2fid"); if(VCAStat == "*" || !((VCAStat == "." && SYS->security().at().usrPresent(userStart())) || (VCAStat != "." && !(err=mod->cntrIfCmd(req,userStart(),userPass(),VCAStat,true))))) //!!! But for remote same the request has the athentification while(true) { if(err == TError::Tr_Connect) { postMess(nodePath().c_str(),_("Error connecting to remote station!")); return NULL; } DlgUser d_usr(userStart().c_str(), userPass().c_str(), VCAStat.c_str()); int rez = d_usr.exec(); if(rez == DlgUser::SelCancel) return NULL; if(rez == DlgUser::SelErr) { postMess(nodePath().c_str(), QString(_("Error authenticating the user '%1'%2!!!")) .arg(d_usr.user()) .arg(d_usr.property("err").toString().size()?", "+d_usr.property("err").toString():"")); continue; } user_open = d_usr.user().toStdString(); user_pass = d_usr.password().toStdString(); if(VCAStat == "*") VCAStat = d_usr.VCAstat.toStdString(); break; } if(!user_open.size()) user_open = req.attr("user"); //Check for run projects need string sprj; unsigned screen; VisRun *fsess = NULL; for(int p_off = 0; (sprj=TSYS::strSepParse(runPrjs(),0,';',&p_off)).size(); ) { // Screen number take screen = 0; size_t iSep = sprj.find("-"); if(iSep != string::npos) screen = s2i(sprj.substr(iSep+1)); sprj = sprj.substr(0, iSep); // Check and extract for "ses_" or "proj_" prefixes bool isSess = false; if(sprj.find("ses_") == 0) { isSess = true; sprj.erase(0,4); } else if(sprj.find("prj_") == 0) sprj.erase(0,4); //QDesktopWidget().screen(1) // Find for already opened run window MtxAlloc res(mnWindsRes, true); bool openRunOK = false; for(unsigned iW = 0; !openRunOK && iW < mnWinds.size(); iW++) openRunOK = (qobject_cast(mnWinds[iW]) && ((isSess && ((VisRun*)mnWinds[iW])->workSess() == sprj) || (!isSess && ((VisRun*)mnWinds[iW])->srcProject() == sprj)) && ((VisRun*)mnWinds[iW])->screen() == screen); res.unlock(); if(openRunOK) continue; VisRun *sess = new VisRun((isSess?"/ses_":"/prj_")+sprj, user_open, user_pass, VCAStat, true, screen); sess->show(); sess->raise(); sess->activateWindow(); if(!fsess) fsess = sess; } return fsess ? (QMainWindow*)fsess : (QMainWindow*)new VisDevelop(user_open, user_pass, VCAStat); } void TVision::modStart( ) { mess_debug(nodePath().c_str(),_("Starting the module.")); mEndRun = false; runSt = true; } void TVision::modStop( ) { mess_debug(nodePath().c_str(),_("Stopping the module.")); mEndRun = true; MtxAlloc res(mnWindsRes, true); for(unsigned iW = 0; iW < mnWinds.size(); iW++) while(mnWinds[iW]) { res.unlock(); if(!SYS->mainThr.freeStat()) qApp->processEvents(); //!!!! Else can lock here the main thread, especially at child windows open TSYS::sysSleep(prmWait_DL); res.lock(); } TSYS::sysSleep(prmWait_DL); runSt = false; } WdgShape *TVision::getWdgShape( const string &iid ) { for(unsigned iSw = 0; iSw < shapesWdg.size(); iSw++) if(shapesWdg[iSw]->id() == iid) return shapesWdg[iSw]; return NULL; } void TVision::regWin( QMainWindow *mwd ) { MtxAlloc res(mnWindsRes, true); unsigned iW; for(iW = 0; iW < mnWinds.size(); iW++) if(mnWinds[iW] == NULL) break; if(iW == mnWinds.size()) mnWinds.push_back((QMainWindow*)NULL); mnWinds[iW] = mwd; } void TVision::unregWin( QMainWindow *mwd ) { MtxAlloc res(mnWindsRes, true); for(unsigned iW = 0; iW < mnWinds.size(); iW++) if(mnWinds[iW] == mwd) mnWinds[iW] = NULL; } void TVision::cntrCmdProc( XMLNode *opt ) { //Get page info if(opt->name() == "info") { TUI::cntrCmdProc(opt); ctrMkNode("fld",opt,-1,"/prm/st/disp_n",_("Number of individual displays"),R_R_R_,"root",SUI_ID,1,"tp","dec"); ctrMkNode("list",opt,-1,"/prm/st/mnWinds",_("Main windows"),R_R_R_,"root",SUI_ID); if(ctrMkNode("area",opt,1,"/prm/cfg",_("Module options"))) { ctrMkNode("fld",opt,-1,"/prm/cfg/exit_on_lst_run_prj_cls",_("Exit when closing the last running project"),RWRWR_,"root",SUI_ID,1,"tp","bool"); ctrMkNode("fld",opt,-1,"/prm/cfg/drop_common_wdg_stls",_("Reset widget styles to common"),RWRWR_,"root",SUI_ID,2,"tp","bool", "help",_("Required for some widget styles like \"gtk\" in some specific widgets in run mode, like buttons for the background color and images.")); ctrMkNode("fld",opt,-1,"/prm/cfg/cachePgLife",_("Lifetime and maximum number of the cached pages"),RWRWR_,"root",SUI_ID,2,"tp","real", "help",_("The time is specified in hours, which defines the inactivity interval for closing pages in the cache.\n" "Zero value of the time excludes the closing of pages in the cache.")); ctrMkNode("fld",opt,-1,"/prm/cfg/cachePgSz","",RWRWR_,"root",SUI_ID,2,"tp","dec", "help",_("The number defines a limit of pages in the cache.\n" "Zero value of the number excludes the cache limit.")); ctrMkNode("comm",opt,-1,"/prm/cfg/host_lnk",_("Go to the remote stations list configuration"),RWRW__,"root",SUI_ID,1,"tp","lnk"); ctrMkNode("fld",opt,-1,"/prm/cfg/stationVCA",_("Station of the VCA engine"),RWRWR_,"root",SUI_ID,5, "tp","str", "idm","1", "dest","select", "select","/prm/cfg/vca_lst", "help",TSYS::strMess(_("Use '%s' for querying the station together user and password at the module generic call.\n" "Useful at presence of many control stations and for opening several of them simultaneously."), _("")); opt->childAdd("el")->setAttr("id",".")->setText(_("")); vector lst; SYS->transport().at().extHostList("*", lst, false, -1, opt->attr("lang")); sort(lst.begin(), lst.end(), compareHosts); for(unsigned iLs = 0; iLs < lst.size(); iLs++) opt->childAdd("el")->setAttr("id", lst[iLs].id)->setText(lst[iLs].name); } else TUI::cntrCmdProc(opt); } bool TVision::compareHosts( const TTransportS::ExtHost &v1, const TTransportS::ExtHost &v2 ) { return v1.name < v2.name; } void TVision::postMess( const QString &cat, const QString &mess, TVision::MessLev type, QWidget *parent ) { //Put system message message(cat.toStdString().c_str(), (type==TVision::Crit) ? TMess::Crit : (type==TVision::Error)?TMess::Error: (type==TVision::Warning)?TMess::Warning:TMess::Info,"%s",mess.toStdString().c_str()); //QT message switch(type) { case TVision::Info: QMessageBox::information(parent, MOD_NAME.c_str(), mess);break; case TVision::Warning: QMessageBox::warning(parent, MOD_NAME.c_str(), mess); break; case TVision::Error: QMessageBox::critical(parent, MOD_NAME.c_str(), mess); break; case TVision::Crit: QErrorMessage::qtHandler()->showMessage(mess); break; } } int TVision::cntrIfCmd( XMLNode &node, const string &user, const string &password, const string &VCAStat, bool glob ) { //Check for local VCAEngine path if(!glob) node.setAttr("path", "/UI/VCAEngine"+node.attr("path")); bool isLoc = (VCAStat.empty() || VCAStat == "."); node.setAttr("path", "/"+(isLoc?SYS->id():VCAStat)+node.attr("path")); if(!isLoc) node.setAttr("lang", Mess->langCode(user)); try { int rez = SYS->transport().at().cntrIfCmd(node, "UIVision", (isLoc?user:("\n"+user+"\n"+password))); //Password's hash processing if(node.attr("pHash").size() && userStart() == user && userPass() != (SEC_HASH_MAGIC+node.attr("pHash"))) { setUserPass(SEC_HASH_MAGIC + node.attr("pHash")); node.setAttr("pHash", ""); } return rez; } catch(TError &err) { node.childClear(); node.setAttr("mcat", err.cat)->setAttr("rez", i2s(TError::Tr_Connect))->setText(err.mess); } return s2i(node.attr("rez")); } QWidget *TVision::getFocusedWdg( QWidget *wcntr ) { while(wcntr->focusProxy()) wcntr = wcntr->focusProxy(); return wcntr; } void TVision::modInfo( vector &list ) { TModule::modInfo(list); list.push_back("SubType"); } string TVision::modInfo( const string &name ) { if(name == "SubType") return SUB_TYPE; return TModule::modInfo(name); }