//OpenSCADA module Special.FLibSYS file: varchfnc.cpp /*************************************************************************** * Copyright (C) 2009-2020 by Roman Savochenko, * * * * 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; version 2 of the License. * * * * 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 "varchfnc.h" #if HAVE_FFTW3_H #include #endif using namespace FLibSYS; //************************************************* //* VArchObj - Value archive object * //************************************************* VArchObj::VArchObj( const string &user ) : mIsArch(false), mBuf(NULL), mUser(user) { if(mess_lev() == TMess::Debug) SYS->cntrIter(objName(), 1); } VArchObj::~VArchObj( ) { close(); if(mess_lev() == TMess::Debug) SYS->cntrIter(objName(), -1); } bool VArchObj::open( const string &inm ) { close(); try { AutoHD ta; AutoHD nd = SYS->nodeAt(inm, 0, '.'); if(dynamic_cast(&nd.at())) ta = dynamic_cast(nd.at()).arch(); else if(dynamic_cast(&nd.at())) ta = nd; if(ta.freeStat()) return false; mArch = new AutoHD(ta); mIsArch = true; } catch(TError &err) { return false; } return true; } bool VArchObj::open( TFld::Type vtp, int isz, int64_t ipr, bool ihgrd, bool ihres ) { close(); if(!(mBuf=new TValBuf(vtp,isz,ipr,ihgrd,ihres))) return false; mIsArch = false; return true; } void VArchObj::close( ) { if(isArch() && mArch) delete mArch; if(!isArch() && mBuf) delete mBuf; mIsArch = false; mBuf = NULL; } AutoHD VArchObj::arch( ) { if(!isArch() || !mArch) return AutoHD(); return *mArch; } TValBuf *VArchObj::buf( ) { if(isArch()) return NULL; return mBuf; } TVariant VArchObj::propGet( const string &id ) { throw TError("VArchObj", _("Properties are not supported by the object.")); } void VArchObj::propSet( const string &id, TVariant val ) { throw TError("VArchObj", _("Properties are not supported by the object.")); } TVariant VArchObj::funcCall( const string &id, vector &prms ) { //bool isNull() - no initiated object if(id == "isNull") return (!isArch() && !buf()); //int begin( int usec = 0, string archivator = "" ) - getting the start time of the archive through the return of seconds // and microseconds for the archivator . if(id == "begin") { int64_t vtm; if(isArch()) vtm = arch().at().begin((prms.size()>=2) ? prms[1].getS() : ""); else { if(!buf()) return (int64_t)EVAL_INT; vtm = buf()->begin(); } if(prms.size() >= 1) { prms[0].setI(vtm%1000000); prms[0].setModify(); } return vtm/1000000; } //int end( int usec = 0, string archivator = "" ) - getting the end time of the archive through the return of seconds // and microseconds for the archivator . if(id == "end") { int64_t vtm; if(isArch()) vtm = arch().at().end((prms.size()>=2) ? prms[1].getS() : ""); else { if(!buf()) return (int64_t)EVAL_INT; vtm = buf()->end(); } if(prms.size() >= 1) { prms[0].setI(vtm%1000000); prms[0].setModify(); } return vtm/1000000; } //int period( int usec = 0, string archivator = "" ) - getting the periodicity of the archive through the return of seconds // and microseconds for the archivator . if(id == "period") { int64_t vper; if(isArch()) vper = arch().at().period((prms.size()>=2) ? prms[1].getS() : ""); else { if(!buf()) return (int64_t)EVAL_INT; vper = buf()->period(); } if(prms.size() >= 1) { prms[0].setI(vper%1000000); prms[0].setModify(); } return vper/1000000; } //ValObj get( int sec, int usec, bool upOrd = false, string archivator = "" ) - getting the value from the archive // at the time : linked to the top for the archivator . // Real time of the value obtained is set in :. if(id == "get" && prms.size() >= 2) { TVariant vl; int64_t vtm = (int64_t)prms[0].getI()*1000000 + prms[1].getI(); if(isArch()) vl = arch().at().getVal(&vtm, (prms.size()>=3)?prms[2].getB():false, (prms.size()>=4)?prms[3].getS():""); else { if(!buf()) return EVAL_REAL; switch(buf()->valType()) { case TFld::Boolean: vl = buf()->getB(&vtm, (prms.size()>=3)?prms[2].getB():false); break; case TFld::Integer: vl = buf()->getI(&vtm, (prms.size()>=3)?prms[2].getB():false); break; case TFld::Real: vl = buf()->getR(&vtm, (prms.size()>=3)?prms[2].getB():false); break; case TFld::String: vl = buf()->getS(&vtm, (prms.size()>=3)?prms[2].getB():false); break; default: break; } } prms[0].setI(vtm/1000000); prms[0].setModify(); prms[1].setI(vtm%1000000); prms[1].setModify(); return vl; } //bool set( ValObj val, int sec, int usec, string archivator = "" ) - writing of the value to the archive for the time : for the archivator . if(id == "set" && prms.size() >= 3) { if(isArch()) { if(!SYS->security().at().access(mUser,SEC_WR,"root",SARH_ID,RWRWR_)) return false; TFld::Type tp = TFld::String; switch(prms[0].type()) { case TVariant::Boolean: tp = TFld::Boolean; break; case TVariant::Integer: tp = TFld::Integer; break; case TVariant::Real: tp = TFld::Real; break; default: break; } TValBuf buf(tp, 10, arch().at().period(), true, true); buf.set(prms[0], (int64_t)prms[1].getI()*1000000+prms[2].getI()); arch().at().setVals(buf, buf.begin(), buf.end(), (prms.size()>=4)?prms[3].getS():""); } else { if(!buf()) return false; switch(buf()->valType()) { case TFld::Boolean: buf()->setB(prms[0].getB(), (int64_t)prms[1].getI()*1000000+prms[2].getI()); break; case TFld::Integer: buf()->setI(prms[0].getI(), (int64_t)prms[1].getI()*1000000+prms[2].getI()); break; case TFld::Real: buf()->setR(prms[0].getR(), (int64_t)prms[1].getI()*1000000+prms[2].getI()); break; case TFld::String: buf()->setS(prms[0].getS(), (int64_t)prms[1].getI()*1000000+prms[2].getI()); break; default: break; } } return true; } //bool copy( VArchObj src, int begSec, int begUSec, int endSec, int endUSec, string archivator = "" ) - copying of the part of // the source archive or its buffer in the current beginning from : and ending with : // for the archivator . if(id == "copy" && prms.size() >= 5) { AutoHD src = prms[0].getO(); if(src.freeStat()) return false; AutoHD tarch; if(src.at().isArch()) { TValBuf* vb = NULL; if(isArch()) { if(!SYS->security().at().access(mUser,SEC_WR,"root",SARH_ID,RWRWR_)) return false; tarch = arch(); vb = &tarch.at(); } else vb = buf(); if(!vb) return false; src.at().arch().at().getVals(*vb, prms[1].getI()*1000000+prms[2].getI(), prms[3].getI()*1000000+prms[4].getI(), (prms.size()>=6)?prms[5].getS():""); } else if(isArch()) { if(!SYS->security().at().access(mUser,SEC_WR,"root",SARH_ID,RWRWR_)) return false; TValBuf* vb = NULL; if(src.at().isArch()) { tarch = src.at().arch(); vb = &tarch.at(); } else vb = src.at().buf(); if(!vb) return false; arch().at().setVals(*vb, prms[1].getI()*1000000+prms[2].getI(), prms[3].getI()*1000000+prms[4].getI(), (prms.size()>=6)?prms[5].getS():""); } else { TValBuf* svb = src.at().buf(); TValBuf* dvb = buf(); if(!svb || !dvb) return false; svb->getVals(*dvb, prms[1].getI()*1000000+prms[2].getI(), prms[3].getI()*1000000+prms[4].getI()); } return true; } #if HAVE_FFTW3_H //ArrayObj FFT( int tm, real size, string archivator = "", int tm_usec = 0, double vlOnEVAL = ) // - performs the Fast Fourier Transformation using the FFT algorithm. // Returns an array of amplitudes of the frequencies for archive's values window for time : (seconds:microseconds), // depth to history (seconds) and for archivator with fill EVALs by . // tm, tm_usec - FFT window time (end); the curent time uses for zero ; // size - size in seconds to history (begin); // archivator - concrete archiver select for FFT processing; // vlOnEVAL - replacing EVAL value; for same is EVAL used previous actual value for the EVAL replace. if(id == "FFT" && prms.size() >= 2) { int64_t etm = 1000000ll * (!prms[0].getI() ? TSYS::curTime()/1000000 : prms[0].getI()) + ((prms.size()>=4) ? prms[3].getI() : 0); int64_t btm = etm - (int64_t)(1e6*prms[1].getR()); int fftN = 0, iN = 0, bufEVAL = 0; double *fftIn = NULL, tVl, vlOnEVAL = (prms.size()>=5) ? prms[4].getR() : EVAL_REAL; bool tracePrevNotEVAL = (vlOnEVAL == EVAL_REAL); if(tracePrevNotEVAL) vlOnEVAL = 0; TArrayObj *ao = new TArrayObj(); if(isArch()) { string archivator = (prms.size()>=3) ? prms[2].getS() : ""; TValBuf tb(TFld::Real, 0, arch().at().period(archivator), true, true); arch().at().getVals(tb, btm, etm, archivator, 600000); fftN = tb.realSize(); if(fftN > 10) { fftIn = (double*)malloc(sizeof(double)*fftN); for(btm = tb.begin(); btm <= tb.end() && iN < fftN; btm++, iN++) if((tVl=tb.getR(&btm,true)) == EVAL_REAL) { fftIn[iN] = vlOnEVAL; bufEVAL++; } else { fftIn[iN] = tVl; if(tracePrevNotEVAL) vlOnEVAL = tVl; } } } else if(buf() && buf()->realSize() > 10) { fftN = buf()->realSize(); fftIn = (double*)malloc(sizeof(double)*fftN); for(btm = buf()->begin(); btm <= buf()->end() && iN < fftN; btm++, iN++) if((tVl=buf()->getR(&btm,true)) == EVAL_REAL) { fftIn[iN] = vlOnEVAL; bufEVAL++; } else { fftIn[iN] = tVl; if(tracePrevNotEVAL) vlOnEVAL = tVl; } } if(fftIn) { fftw_complex *fftOut = (fftw_complex*)malloc(sizeof(fftw_complex)*(fftN/2+1)); fftw_plan p = fftw_plan_dft_r2c_1d(fftN, fftIn, fftOut, FFTW_ESTIMATE); fftw_execute(p); fftw_destroy_plan(p); for(int iV = 0; iV < (fftN/2+1); iV++) if(!iV) ao->arSet(iV, fftOut[iV][0]/fftN); else ao->arSet(iV, pow(pow(fftOut[iV][0],2)+pow(fftOut[iV][1],2),0.5)/(fftN/2)); free(fftIn); free(fftOut); ao->propSet("bufSize", fftN); ao->propSet("bufEVAL", bufEVAL); } return ao; } #endif throw TError("VArchObj",_("Error the function '%s' or missing its parameters."),id.c_str()); }